aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/common_test/doc/src/ct.xml13
-rw-r--r--lib/common_test/doc/src/ct_run.xml5
-rw-r--r--lib/common_test/doc/src/ct_telnet.xml5
-rw-r--r--lib/common_test/doc/src/notes.xml62
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml9
-rw-r--r--lib/common_test/doc/src/unix_telnet.xml3
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml12
-rw-r--r--lib/common_test/src/ct.erl6
-rw-r--r--lib/common_test/src/ct_groups.erl2
-rw-r--r--lib/common_test/src/ct_hooks.erl6
-rw-r--r--lib/common_test/src/ct_logs.erl78
-rw-r--r--lib/common_test/src/ct_master.erl12
-rw-r--r--lib/common_test/src/ct_run.erl58
-rw-r--r--lib/common_test/src/ct_telnet.erl46
-rw-r--r--lib/common_test/src/ct_telnet_client.erl17
-rw-r--r--lib/common_test/src/ct_testspec.erl7
-rw-r--r--lib/common_test/src/ct_util.erl1
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/src/cth_log_redirect.erl18
-rw-r--r--lib/common_test/src/cth_surefire.erl3
-rw-r--r--lib/common_test/src/test_server.erl15
-rw-r--r--lib/common_test/src/test_server_gl.erl6
-rw-r--r--lib/common_test/src/unix_telnet.erl16
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl96
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl2
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl2
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE.erl7
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl16
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl32
-rw-r--r--lib/common_test/test/ct_pre_post_test_io_SUITE.erl126
-rw-r--r--lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl8
-rw-r--r--lib/common_test/test/ct_surefire_SUITE.erl109
-rw-r--r--lib/common_test/test/ct_surefire_SUITE_data/fail_SUITE.erl (renamed from lib/compiler/test/compilation_SUITE_data/bin_syntax_1.erl)26
-rw-r--r--lib/common_test/test/ct_surefire_SUITE_data/fail_pre_init_per_suite.erl47
-rw-r--r--lib/common_test/test/ct_surefire_SUITE_data/pass_SUITE.erl (renamed from lib/compiler/test/compilation_SUITE_data/beam_compiler_5.erl)23
-rw-r--r--lib/common_test/test/ct_test_support.erl21
-rw-r--r--lib/common_test/test_server/ts_run.erl13
-rw-r--r--lib/compiler/src/beam_asm.erl4
-rw-r--r--lib/compiler/src/cerl.erl13
-rw-r--r--lib/compiler/src/cerl_trees.erl16
-rw-r--r--lib/compiler/src/compile.erl5
-rw-r--r--lib/compiler/src/core_parse.yrl61
-rw-r--r--lib/compiler/src/core_pp.erl127
-rw-r--r--lib/compiler/src/rec_env.erl3
-rw-r--r--lib/compiler/src/v3_core.erl25
-rw-r--r--lib/compiler/src/v3_kernel.erl3
-rw-r--r--lib/compiler/test/beam_block_SUITE.erl117
-rw-r--r--lib/compiler/test/beam_utils_SUITE.erl40
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl96
-rw-r--r--lib/compiler/test/compilation_SUITE.erl375
-rw-r--r--lib/compiler/test/compilation_SUITE_data/beam_compiler_1.erl32
-rw-r--r--lib/compiler/test/compilation_SUITE_data/beam_compiler_12.erl30
-rw-r--r--lib/compiler/test/compilation_SUITE_data/beam_compiler_2.erl36
-rw-r--r--lib/compiler/test/compilation_SUITE_data/beam_compiler_3.erl30
-rw-r--r--lib/compiler/test/compilation_SUITE_data/bin_syntax_2.erl42
-rw-r--r--lib/compiler/test/compilation_SUITE_data/bin_syntax_3.erl36
-rw-r--r--lib/compiler/test/compilation_SUITE_data/bin_syntax_4.erl33
-rw-r--r--lib/compiler/test/compilation_SUITE_data/bin_syntax_6.erl40
-rw-r--r--lib/compiler/test/compilation_SUITE_data/catch_in_catch.erl52
-rw-r--r--lib/compiler/test/compilation_SUITE_data/compiler_3.erl34
-rw-r--r--lib/compiler/test/compilation_SUITE_data/compiler_5.erl50
-rw-r--r--lib/compiler/test/compilation_SUITE_data/complex_guard.erl32
-rw-r--r--lib/compiler/test/compilation_SUITE_data/guards.erl107
-rw-r--r--lib/compiler/test/compilation_SUITE_data/long_string.erl671
-rw-r--r--lib/compiler/test/compilation_SUITE_data/nested_tuples_in_case_expr.erl37
-rw-r--r--lib/compiler/test/compilation_SUITE_data/otp_2141.erl25
-rw-r--r--lib/compiler/test/compilation_SUITE_data/otp_2173.erl32
-rw-r--r--lib/compiler/test/compilation_SUITE_data/otp_5076.erl28
-rw-r--r--lib/compiler/test/compilation_SUITE_data/otp_5092.erl40
-rw-r--r--lib/compiler/test/compilation_SUITE_data/otp_5244.erl48
-rw-r--r--lib/compiler/test/compilation_SUITE_data/otp_6121a.erl33
-rw-r--r--lib/compiler/test/compilation_SUITE_data/otp_6121b.erl34
-rw-r--r--lib/compiler/test/compilation_SUITE_data/pattern_expr.erl31
-rw-r--r--lib/compiler/test/compilation_SUITE_data/trycatch_4.erl51
-rw-r--r--lib/compiler/test/compile_SUITE.erl162
-rw-r--r--lib/compiler/test/compile_SUITE_data/missing_testheap1.erl36
-rw-r--r--lib/compiler/test/compile_SUITE_data/missing_testheap2.erl30
-rw-r--r--lib/compiler/test/guard_SUITE.erl25
-rw-r--r--lib/compiler/test/lc_SUITE.erl12
-rw-r--r--lib/compiler/test/match_SUITE.erl67
-rw-r--r--lib/compiler/test/misc_SUITE.erl3
-rw-r--r--lib/compiler/test/record_SUITE.erl60
-rw-r--r--lib/compiler/test/trycatch_SUITE.erl89
-rw-r--r--lib/crypto/c_src/crypto.c22
-rw-r--r--lib/crypto/doc/src/crypto.xml18
-rw-r--r--lib/crypto/src/crypto.erl21
-rw-r--r--lib/crypto/test/crypto_SUITE.erl5
-rw-r--r--lib/crypto/test/old_crypto_SUITE.erl4
-rw-r--r--lib/dialyzer/README2
-rw-r--r--lib/dialyzer/doc/about.txt2
-rw-r--r--lib/dialyzer/doc/manual.txt2
-rw-r--r--lib/dialyzer/info2
-rw-r--r--lib/dialyzer/src/dialyzer.app.src8
-rw-r--r--lib/dialyzer/src/dialyzer.erl3
-rw-r--r--lib/dialyzer/src/dialyzer.hrl4
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl92
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl275
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl1
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl589
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl89
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return2
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/proper/compile_flags.hrl2
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_common.hrl55
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_gen.erl611
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_internal.hrl98
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_types.erl1353
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl2411
-rw-r--r--lib/dialyzer/test/map_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/bad_argument5
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/contract7
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/contract_violation3
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/exact3
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/guard_update5
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/initial_dataflow4
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/is_map_guard5
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/map_galore28
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/map_in_guard4
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/map_in_guard213
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/map_size13
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/maps_merge11
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/opaque_key15
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/order17
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/subtract_value_flip0
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/typeflow4
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/typeflow213
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/typesig5
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/bad_argument.erl19
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/bug.erl63
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/contract.erl14
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/contract_violation.erl29
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/exact.erl23
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/guard_update.erl18
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/initial_dataflow.erl11
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/is_map_guard.erl17
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/map_galore.erl2824
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/map_in_guard.erl35
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/map_in_guard2.erl27
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/map_size.erl36
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/maps_merge.erl29
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl69
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_use.erl97
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/order.erl56
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/subtract_value_flip.erl9
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/typeflow.erl25
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/typeflow2.erl88
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/typesig.erl9
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/literals3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/maps14
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/maps_difftype2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/maps_sum2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/literals.erl6
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/maps1.erl2
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/src/base/diameter.erl29
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl57
-rw-r--r--lib/diameter/src/base/diameter_service.erl24
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl30
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl64
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl251
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl8
-rw-r--r--lib/edoc/src/edoc.erl15
-rw-r--r--lib/edoc/src/edoc_extract.erl48
-rw-r--r--lib/edoc/src/edoc_specs.erl20
-rw-r--r--lib/eldap/vsn.mk2
-rw-r--r--lib/et/src/et.app.src4
-rw-r--r--lib/et/src/et_selector.erl46
-rw-r--r--lib/et/test/et_SUITE.erl24
-rw-r--r--lib/et/test/et_wx_SUITE.erl23
-rw-r--r--lib/eunit/src/eunit_lib.erl6
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl113
-rw-r--r--lib/hipe/cerl/erl_types.erl849
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_match_compiler.erl1235
-rw-r--r--lib/hipe/test/hipe_SUITE.erl3
-rw-r--r--lib/hipe/test/hipe_testsuite_driver.erl2
-rw-r--r--lib/inets/doc/src/mod_esi.xml66
-rw-r--r--lib/inets/doc/src/notes.xml35
-rw-r--r--lib/inets/src/http_server/httpd_example.erl18
-rw-r--r--lib/inets/src/http_server/httpd_script_env.erl14
-rw-r--r--lib/inets/src/inets_app/Makefile3
-rw-r--r--lib/inets/src/inets_app/inets.app.src1
-rw-r--r--lib/inets/src/inets_app/inets.appup.src2
-rw-r--r--lib/inets/src/inets_app/inets_regexp.erl414
-rw-r--r--lib/inets/test/httpd_SUITE.erl14
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/java_src/pom.xml.src42
-rw-r--r--lib/kernel/src/code_server.erl465
-rw-r--r--lib/kernel/src/dist_util.erl8
-rw-r--r--lib/kernel/src/global_group.erl6
-rw-r--r--lib/kernel/src/rpc.erl39
-rw-r--r--lib/kernel/test/code_SUITE.erl216
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl13
-rw-r--r--lib/kernel/test/global_group_SUITE.erl10
-rw-r--r--lib/kernel/test/seq_trace_SUITE.erl16
-rw-r--r--lib/mnesia/src/Makefile2
-rw-r--r--lib/mnesia/src/mnesia.app.src2
-rw-r--r--lib/mnesia/src/mnesia.erl144
-rw-r--r--lib/mnesia/src/mnesia.hrl3
-rw-r--r--lib/mnesia/src/mnesia_backend_type.erl115
-rw-r--r--lib/mnesia/src/mnesia_bup.erl270
-rw-r--r--lib/mnesia/src/mnesia_checkpoint.erl23
-rw-r--r--lib/mnesia/src/mnesia_controller.erl14
-rw-r--r--lib/mnesia/src/mnesia_dumper.erl427
-rw-r--r--lib/mnesia/src/mnesia_ext_sup.erl59
-rw-r--r--lib/mnesia/src/mnesia_frag.erl67
-rw-r--r--lib/mnesia/src/mnesia_index.erl435
-rw-r--r--lib/mnesia/src/mnesia_lib.erl168
-rw-r--r--lib/mnesia/src/mnesia_loader.erl240
-rw-r--r--lib/mnesia/src/mnesia_log.erl25
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl36
-rw-r--r--lib/mnesia/src/mnesia_schema.erl994
-rw-r--r--lib/mnesia/src/mnesia_sup.erl8
-rw-r--r--lib/mnesia/src/mnesia_tm.erl226
-rw-r--r--lib/mnesia/test/Makefile3
-rw-r--r--lib/mnesia/test/ext_test.erl237
-rw-r--r--lib/mnesia/test/mnesia_config_test.erl28
-rw-r--r--lib/mnesia/test/mnesia_consistency_test.erl6
-rw-r--r--lib/mnesia/test/mnesia_dirty_access_test.erl68
-rw-r--r--lib/mnesia/test/mnesia_evil_coverage_test.erl150
-rw-r--r--lib/mnesia/test/mnesia_recovery_test.erl4
-rw-r--r--lib/mnesia/test/mnesia_test_lib.erl11
-rw-r--r--lib/mnesia/test/mnesia_test_lib.hrl2
-rw-r--r--lib/mnesia/test/mnesia_trans_access_test.erl11
-rw-r--r--lib/observer/src/observer_alloc_wx.erl57
-rw-r--r--lib/observer/src/observer_app_wx.erl4
-rw-r--r--lib/observer/src/observer_lib.erl103
-rw-r--r--lib/observer/src/observer_perf_wx.erl9
-rw-r--r--lib/observer/src/observer_procinfo.erl4
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl10
-rw-r--r--lib/observer/test/ttb_SUITE.erl57
-rw-r--r--lib/os_mon/c_src/ferrule.c1
-rw-r--r--lib/os_mon/c_src/mod_syslog.c1
-rw-r--r--lib/os_mon/src/cpu_sup.erl2
-rw-r--r--lib/public_key/doc/src/public_key.xml2
-rw-r--r--lib/public_key/doc/src/using_public_key.xml2
-rw-r--r--lib/public_key/test/public_key_SUITE.erl4
-rw-r--r--lib/runtime_tools/c_src/Makefile.in2
-rw-r--r--lib/runtime_tools/c_src/dyntrace.c604
-rw-r--r--lib/runtime_tools/c_src/dyntrace_lttng.h367
-rw-r--r--lib/runtime_tools/doc/src/LTTng.xml461
-rw-r--r--lib/runtime_tools/doc/src/Makefile2
-rw-r--r--lib/runtime_tools/doc/src/part.xml1
-rw-r--r--lib/runtime_tools/src/dbg.erl29
-rw-r--r--lib/runtime_tools/src/dyntrace.erl79
-rw-r--r--lib/runtime_tools/src/msacc.erl18
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src4
-rw-r--r--lib/runtime_tools/src/system_information.erl52
-rw-r--r--lib/runtime_tools/test/Makefile1
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl1379
-rw-r--r--lib/runtime_tools/test/dyntrace_SUITE.erl31
-rw-r--r--lib/runtime_tools/test/dyntrace_lttng_SUITE.erl377
-rw-r--r--lib/runtime_tools/test/erts_alloc_config_SUITE.erl162
-rw-r--r--lib/runtime_tools/test/runtime_tools_SUITE.erl39
-rw-r--r--lib/runtime_tools/test/system_information_SUITE.erl10
-rw-r--r--lib/sasl/doc/src/appup.xml3
-rw-r--r--lib/sasl/doc/src/error_logging.xml5
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl17
-rw-r--r--lib/ssh/doc/src/ssh.xml17
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml216
-rw-r--r--lib/ssh/src/ssh.app.src9
-rw-r--r--lib/ssh/src/ssh.erl99
-rw-r--r--lib/ssh/src/ssh.hrl4
-rw-r--r--lib/ssh/src/ssh_acceptor.erl45
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl5
-rw-r--r--lib/ssh/src/ssh_auth.erl22
-rw-r--r--lib/ssh/src/ssh_channel.erl5
-rw-r--r--lib/ssh/src/ssh_connect.hrl8
-rw-r--r--lib/ssh/src/ssh_connection.erl152
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl2901
-rw-r--r--lib/ssh/src/ssh_info.erl2
-rw-r--r--lib/ssh/src/ssh_message.erl46
-rw-r--r--lib/ssh/src/ssh_no_io.erl44
-rw-r--r--lib/ssh/src/ssh_system_sup.erl9
-rw-r--r--lib/ssh/src/ssh_transport.erl195
-rw-r--r--lib/ssh/src/sshc_sup.erl4
-rw-r--r--lib/ssh/test/Makefile1
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl33
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE.erl46
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl36
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl61
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE.erl3
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl27
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl7
-rw-r--r--lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl1
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl91
-rw-r--r--lib/ssh/test/ssh_test_cli.erl9
-rw-r--r--lib/ssh/test/ssh_test_lib.erl78
-rw-r--r--lib/ssh/test/ssh_test_lib.hrl27
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl42
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key16
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key.pub5
-rw-r--r--lib/ssh/test/ssh_trpt_test_lib.erl9
-rw-r--r--lib/ssh/test/ssh_upgrade_SUITE.erl3
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml47
-rw-r--r--lib/ssl/src/dtls_connection.erl354
-rw-r--r--lib/ssl/src/dtls_record.erl25
-rw-r--r--lib/ssl/src/ssl.appup.src6
-rw-r--r--lib/ssl/src/ssl.erl32
-rw-r--r--lib/ssl/src/ssl_cipher.erl96
-rw-r--r--lib/ssl/src/ssl_connection.erl950
-rw-r--r--lib/ssl/src/ssl_connection.hrl5
-rw-r--r--lib/ssl/src/ssl_handshake.erl12
-rw-r--r--lib/ssl/src/ssl_internal.hrl2
-rw-r--r--lib/ssl/src/ssl_manager.erl6
-rw-r--r--lib/ssl/src/ssl_record.erl2
-rw-r--r--lib/ssl/src/tls_connection.erl608
-rw-r--r--lib/ssl/src/tls_handshake.erl5
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl212
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl133
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl27
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/Makefile3
-rw-r--r--lib/stdlib/doc/src/binary.xml2
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml14
-rw-r--r--lib/stdlib/doc/src/gen_server.xml1
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml1671
-rw-r--r--lib/stdlib/doc/src/lists.xml15
-rw-r--r--lib/stdlib/doc/src/maps.xml60
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml6
-rw-r--r--lib/stdlib/doc/src/ref_man.xml3
-rw-r--r--lib/stdlib/doc/src/specs.xml1
-rw-r--r--lib/stdlib/doc/src/supervisor.xml8
-rw-r--r--lib/stdlib/doc/src/sys.xml29
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl4
-rw-r--r--lib/stdlib/include/assert.hrl29
-rw-r--r--lib/stdlib/src/Makefile3
-rw-r--r--lib/stdlib/src/epp.erl72
-rw-r--r--lib/stdlib/src/erl_lint.erl97
-rw-r--r--lib/stdlib/src/erl_parse.yrl12
-rw-r--r--lib/stdlib/src/erl_pp.erl26
-rw-r--r--lib/stdlib/src/gen.erl119
-rw-r--r--lib/stdlib/src/gen_event.erl9
-rw-r--r--lib/stdlib/src/gen_fsm.erl83
-rw-r--r--lib/stdlib/src/gen_server.erl110
-rw-r--r--lib/stdlib/src/gen_statem.erl1308
-rw-r--r--lib/stdlib/src/io_lib.erl16
-rw-r--r--lib/stdlib/src/lists.erl15
-rw-r--r--lib/stdlib/src/maps.erl59
-rw-r--r--lib/stdlib/src/otp_internal.erl7
-rw-r--r--lib/stdlib/src/proc_lib.erl18
-rw-r--r--lib/stdlib/src/rand.erl10
-rw-r--r--lib/stdlib/src/stdlib.app.src1
-rw-r--r--lib/stdlib/src/supervisor.erl4
-rw-r--r--lib/stdlib/test/Makefile1
-rw-r--r--lib/stdlib/test/epp_SUITE.erl63
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl156
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl23
-rw-r--r--lib/stdlib/test/error_logger_forwarder.erl6
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl1584
-rw-r--r--lib/stdlib/test/lists_SUITE.erl16
-rw-r--r--lib/stdlib/test/maps_SUITE.erl56
-rw-r--r--lib/stdlib/test/rand_SUITE.erl2
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl374
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl1410
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl181
-rw-r--r--lib/syntax_tools/src/igor.erl13
-rw-r--r--lib/syntax_tools/src/syntax_tools.app.src2
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl25
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/empty.erl1
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/igor_type_specs.erl80
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl84
-rw-r--r--lib/tools/doc/src/erlang_mode.xml1
-rw-r--r--lib/tools/emacs/erlang-skels.el118
-rw-r--r--lib/tools/emacs/erlang.el15
-rw-r--r--lib/tools/src/fprof.erl17
-rw-r--r--lib/tools/test/fprof_SUITE.erl40
-rw-r--r--lib/wx/api_gen/wx_extra/wxEvtHandler.c_src2
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl12
-rw-r--r--lib/wx/api_gen/wxapi.conf6
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp42
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h3834
-rw-r--r--lib/wx/c_src/wxe_helpers.cpp4
-rw-r--r--lib/wx/c_src/wxe_helpers.h2
-rw-r--r--lib/wx/c_src/wxe_impl.cpp5
-rw-r--r--lib/wx/c_src/wxe_main.cpp4
-rw-r--r--lib/wx/examples/demo/ex_canvas.erl13
-rw-r--r--lib/wx/examples/demo/ex_canvas_paint.erl16
-rw-r--r--lib/wx/src/gen/wxGauge.erl39
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl3834
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl3834
-rw-r--r--lib/wx/src/wx.erl6
-rw-r--r--lib/wx/src/wx_object.erl8
-rw-r--r--lib/wx/test/wx_app_SUITE.erl10
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl17
-rw-r--r--lib/wx/test/wx_class_SUITE.erl71
-rw-r--r--lib/wx/test/wx_event_SUITE.erl2
-rw-r--r--lib/wx/test/wx_obj_test.erl11
-rw-r--r--lib/wx/test/wx_opengl_SUITE.erl2
-rw-r--r--lib/wx/test/wx_xtra_SUITE.erl2
-rw-r--r--lib/wx/test/wxt.erl12
392 files changed, 36871 insertions, 14950 deletions
diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml
index 23aeaa38ea..5231ef24a4 100644
--- a/lib/common_test/doc/src/ct.xml
+++ b/lib/common_test/doc/src/ct.xml
@@ -601,18 +601,21 @@
</func>
<func>
- <name>get_timetrap_info() -&gt; {Time, Scale}</name>
+ <name>get_timetrap_info() -&gt; {Time, {Scaling,ScaleVal}}</name>
<fsummary>Reads information about the timetrap set for the current
test case.</fsummary>
<type>
<v>Time = integer() | infinity</v>
- <v>Scale = true | false</v>
+ <v>Scaling = true | false</v>
+ <v>ScaleVal = integer()</v>
</type>
<desc><marker id="get_timetrap_info-0"/>
<p>Reads information about the timetrap set for the current test
- case. <c>Scale</c> indicates if <c>Common Test</c> will attempt
+ case. <c>Scaling</c> indicates if <c>Common Test</c> will attempt
to compensate timetraps automatically for runtime delays
- introduced by, for example, tools like cover.</p>
+ introduced by, for example, tools like cover. <c>ScaleVal</c> is
+ the value of the current scaling multipler (always 1 if scaling is
+ disabled). Note the <c>Time</c> is not the scaled result.</p>
</desc>
</func>
@@ -1136,7 +1139,7 @@
Opts.</fsummary>
<type>
<v>Opts = [OptTuples]</v>
- <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
+ <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {esc_chars, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
<v>TestDirs = [string()] | string()</v>
<v>Suites = [string()] | [atom()] | string() | atom()</v>
<v>Cases = [atom()] | atom()</v>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index 88873ce5b2..9e6229f1dd 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -124,6 +124,7 @@
[-duration HHMMSS [-force_stop [skip_rest]]] |
[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
+ [-no_esc_chars]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
[-exit_status ignore_config]
@@ -162,6 +163,7 @@
[-duration HHMMSS [-force_stop [skip_rest]]] |
[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
+ [-no_esc_chars]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
[-exit_status ignore_config]</pre>
@@ -186,7 +188,8 @@
[-muliply_timetraps Multiplier]
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
- [-basic_html]</pre>
+ [-basic_html]
+ [-no_esc_chars]</pre>
</section>
<section>
diff --git a/lib/common_test/doc/src/ct_telnet.xml b/lib/common_test/doc/src/ct_telnet.xml
index b35e511501..e2a45e894b 100644
--- a/lib/common_test/doc/src/ct_telnet.xml
+++ b/lib/common_test/doc/src/ct_telnet.xml
@@ -64,6 +64,8 @@
remaining string terminated) = 0</p></item>
<item><p>Polling interval (sleep time between polls) = 1 second</p>
</item>
+ <item><p>The TCP_NODELAY option for the telnet socket
+ is disabled (set to <c>false</c>) per default</p></item>
</list>
<p>These parameters can be modified by the user with the following
@@ -76,7 +78,8 @@
{reconnection_interval,Millisec},
{keep_alive,Bool},
{poll_limit,N},
- {poll_interval,Millisec}]}.</pre>
+ {poll_interval,Millisec},
+ {tcp_nodelay,Bool}]}.</pre>
<p><c>Millisec = integer(), N = integer()</c></p>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index aba71b0d4a..ebba864606 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,68 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.12.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>nodelay</c> option used to be enabled
+ (<c>true</c>) by default for sockets opened by the Common
+ Test telnet client. This appeared to cause communication
+ problems with telnet servers on some systems, and
+ therefore the option is no longer used. Its value may
+ instead be specified in the telnet connection settings.
+ See the man page for <c>ct_telnet</c> for details. Please
+ note that the interface function <c>connect</c> in
+ <c>unix_telnet</c> has been updated with an extra
+ argument and is now <c>unix_telnet:connect/7</c>.</p>
+ <p>
+ Own Id: OTP-13462 Aux Id: seq13077 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in cth_surefire: When a pre_init_per_suite hook
+ fails before reaching the
+ cth_surefire:pre_init_per_suite, cth_surefire produced
+ incorrect XML.</p>
+ <p>
+ Own Id: OTP-13513</p>
+ </item>
+ <item>
+ <p>
+ The <c>ct:get_timetrap_info/0</c> function has been
+ updated to return more information about timetrap
+ scaling.</p>
+ <p>
+ Own Id: OTP-13535</p>
+ </item>
+ <item>
+ <p>
+ A problem with stylesheet HTML tags getting incorrectly
+ escaped by Common Test has been corrected.</p>
+ <p>
+ Own Id: OTP-13536</p>
+ </item>
+ <item>
+ <p>
+ The <c>ct_run</c> start flag <c>-no_esc_chars</c> and
+ <c>ct:run_test/1</c> start option <c>{esc_chars,Bool}</c>
+ have been introduced to make it possible to disable
+ automatic escaping of characters. Automatic escaping of
+ special HTML characters printed with <c>io:format/1,2</c>
+ and <c>ct:pal/1,2,3,4</c> was introduced in Common Test
+ 1.12. The new flag/option may be used to disable this
+ feature for backwards compatibility reasons. (The option
+ is also supported in test specifications).</p>
+ <p>
+ Own Id: OTP-13537</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.12</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 5ae32ee230..43e36adfb6 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -266,6 +266,10 @@
<tag><c><![CDATA[-verbosity <levels>]]></c></tag>
<item><p>Sets <seealso marker="write_test_chapter#logging">verbosity levels
for printouts</seealso>.</p></item>
+
+ <tag><c><![CDATA[-no_esc_chars]]></c></tag>
+ <item><p>Disables automatic escaping of special HTML characters.
+ See the <seealso marker="write_test_chapter#logging">Logging chapter</seealso>.</p></item>
</taglist>
<note><p>Directories passed to <c>Common Test</c> can have either relative or absolute paths.</p></note>
@@ -802,7 +806,7 @@
program</seealso> for an overview of available start flags
(as most flags have a corresponding configuration term)</item>
<item><seealso marker="write_test_chapter#logging">Logging</seealso>
- (for terms <c>verbosity</c>, <c>stylesheet</c> and <c>basic_html</c>)</item>
+ (for terms <c>verbosity</c>, <c>stylesheet</c>, <c>basic_html</c> and <c>esc_chars</c>)</item>
<item><seealso marker="config_file_chapter#top">External Configuration Data</seealso>
(for terms <c>config</c> and <c>userconfig</c>)</item>
<item><seealso marker="event_handler_chapter#event_handling">Event
@@ -887,6 +891,9 @@
{basic_html, Bool}.
{basic_html, NodeRefs, Bool}.
+ {esc_chars, Bool}.
+ {esc_chars, NodeRefs, Bool}.
+
{release_shell, Bool}.</pre>
<p><em>Test terms:</em></p>
diff --git a/lib/common_test/doc/src/unix_telnet.xml b/lib/common_test/doc/src/unix_telnet.xml
index 701d5ec88e..b2314a53ec 100644
--- a/lib/common_test/doc/src/unix_telnet.xml
+++ b/lib/common_test/doc/src/unix_telnet.xml
@@ -80,7 +80,7 @@
<funcs>
<func>
- <name>connect(ConnName, Ip, Port, Timeout, KeepAlive, Extra) -&gt; {ok, Handle} | {error, Reason}</name>
+ <name>connect(ConnName, Ip, Port, Timeout, KeepAlive, TCPNoDelay, Extra) -&gt; {ok, Handle} | {error, Reason}</name>
<fsummary>Callback for ct_telnet.erl.</fsummary>
<type>
<v>ConnName = target_name()</v>
@@ -88,6 +88,7 @@
<v>Port = integer()</v>
<v>Timeout = integer()</v>
<v>KeepAlive = bool()</v>
+ <v>TCPNoDelay = bool()</v>
<v>Extra = target_name() | {Username, Password}</v>
<v>Username = string()</v>
<v>Password = string()</v>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 40e02347f2..83daf771a6 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -1047,9 +1047,15 @@
<p>Common Test will escape special HTML characters (&lt;, &gt; and &amp;) in printouts
to the log file made with <c>ct:pal/4</c> and <c>io:format/2</c>. In order to print
- strings with HTML tags to the log, use the <c>ct:log/5</c> function. The character escaping
- feature is per default disabled for <c>ct:log/5</c>, but can be enabled with the
- <c>esc_chars</c> option.</p>
+ strings with HTML tags to the log, use the <c>ct:log/3,4,5</c> function. The character
+ escaping feature is per default disabled for <c>ct:log/3,4,5</c> but can be enabled with
+ the <c>esc_chars</c> option in the <c>Opts</c> list, see <seealso marker="ct#log-5">
+ <c>ct:log/3,4,5</c></seealso>.</p>
+
+ <p>If the character escaping feature needs to be disabled (typically for backwards
+ compatibility reasons), use the <c>ct_run</c> start flag <c>-no_esc_chars</c>, or the
+ <c>ct:run_test/1</c> start option <c>{esc_chars,Bool}</c> (this start option is also
+ supported in test specifications).</p>
<p>For more information about log files, see section
<seealso marker="run_test_chapter#log_files">Log Files</seealso>
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 7fe90b60ff..cae7bea406 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -161,9 +161,9 @@ run(TestDirs) ->
%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
%%% {force_stop,ForceStop} | {decrypt,DecryptKeyOrFile} |
%%% {refresh_logs,LogDir} | {logopts,LogOpts} |
-%%% {verbosity,VLevels} | {basic_html,Bool} |
-%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} |
-%%% {release_shell,Bool}
+%%% {verbosity,VLevels} | {basic_html,Bool} |
+%%% {esc_chars,Bool} | {ct_hooks, CTHs} |
+%%% {enable_builtin_hooks,Bool} | {release_shell,Bool}
%%% TestDirs = [string()] | string()
%%% Suites = [string()] | [atom()] | string() | atom()
%%% Cases = [atom()] | atom()
diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl
index 80f5c30c2a..dd04c5410a 100644
--- a/lib/common_test/src/ct_groups.erl
+++ b/lib/common_test/src/ct_groups.erl
@@ -325,7 +325,7 @@ modify_tc_list1(GrSpecTs, TSCs) ->
true ->
{[TC|TSCs1],lists:delete(TC,GrSpecTs2)};
false ->
- case lists:keymember(TC, 2, GrSpecTs) of
+ case lists:keysearch(TC, 2, GrSpecTs) of
{value,Test} ->
{[Test|TSCs1],
lists:keydelete(TC, 2, GrSpecTs2)};
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index d8f583455b..5422d449fd 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -67,6 +67,8 @@ terminate(Hooks) ->
%% tests.
-spec init_tc(Mod :: atom(),
FuncSpec :: atom() |
+ {ConfigFunc :: init_per_testcase | end_per_testcase,
+ TestCase :: atom()} |
{ConfigFunc :: init_per_group | end_per_group,
GroupName :: atom(),
Properties :: list()},
@@ -103,7 +105,9 @@ init_tc(_Mod, TC = error_in_suite, Config) ->
%% @doc Called as each test case is completed. This includes all configuration
%% tests.
-spec end_tc(Mod :: atom(),
- FuncSpec :: atom() |
+ FuncSpec :: atom() |
+ {ConfigFunc :: init_per_testcase | end_per_testcase,
+ TestCase :: atom()} |
{ConfigFunc :: init_per_group | end_per_group,
GroupName :: atom(),
Properties :: list()},
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 77828a2186..29b38e9748 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -609,7 +609,8 @@ log_timestamp({MS,S,US}) ->
ct_log_fd,
tc_groupleaders,
stylesheet,
- async_print_jobs}).
+ async_print_jobs,
+ tc_esc_chars}).
logger(Parent, Mode, Verbosity) ->
register(?MODULE,self()),
@@ -728,14 +729,18 @@ logger(Parent, Mode, Verbosity) ->
end
end || {Cat,VLvl} <- Verbosity],
io:nl(CtLogFd),
-
+ TcEscChars = case application:get_env(common_test, esc_chars) of
+ {ok,ECBool} -> ECBool;
+ _ -> true
+ end,
logger_loop(#logger_state{parent=Parent,
log_dir=AbsDir,
start_time=Time,
orig_GL=group_leader(),
ct_log_fd=CtLogFd,
tc_groupleaders=[],
- async_print_jobs=[]}).
+ async_print_jobs=[],
+ tc_esc_chars=TcEscChars}).
copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) ->
case file:copy(SrcF, DestF) of
@@ -761,20 +766,21 @@ logger_loop(State) ->
end,
if Importance >= (100-VLvl) ->
CtLogFd = State#logger_state.ct_log_fd,
+ DoEscChars = State#logger_state.tc_esc_chars and EscChars,
case get_groupleader(Pid, GL, State) of
{tc_log,TCGL,TCGLs} ->
case erlang:is_process_alive(TCGL) of
true ->
State1 = print_to_log(SyncOrAsync, Pid,
Category, TCGL, Content,
- EscChars, State),
+ DoEscChars, State),
logger_loop(State1#logger_state{
tc_groupleaders = TCGLs});
false ->
%% Group leader is dead, so write to the
%% CtLog or unexpected_io log instead
unexpected_io(Pid, Category, Importance,
- Content, CtLogFd, EscChars),
+ Content, CtLogFd, DoEscChars),
logger_loop(State)
end;
@@ -783,7 +789,7 @@ logger_loop(State) ->
%% to ct_log, else write to unexpected_io
%% log
unexpected_io(Pid, Category, Importance, Content,
- CtLogFd, EscChars),
+ CtLogFd, DoEscChars),
logger_loop(State#logger_state{
tc_groupleaders = TCGLs})
end;
@@ -794,7 +800,7 @@ logger_loop(State) ->
%% make sure no IO for this test case from the
%% CT logger gets rejected
test_server:permit_io(GL, self()),
- print_style(GL, State#logger_state.stylesheet),
+ print_style(GL,GL,State#logger_state.stylesheet),
set_evmgr_gl(GL),
TCGLs = add_tc_gl(TCPid,GL,State),
if not RefreshLog ->
@@ -882,7 +888,7 @@ create_io_fun(FromPid, CtLogFd, EscChars) ->
{_HdOrFt,S,A} -> {false,S,A};
{S,A} -> {true,S,A}
end,
- try io_lib:format(Str,Args) of
+ try io_lib:format(Str, Args) of
IoStr when Escapable, EscChars, IoList == [] ->
escape_chars(IoStr);
IoStr when Escapable, EscChars ->
@@ -925,7 +931,12 @@ print_to_log(sync, FromPid, Category, TCGL, Content, EscChars, State) ->
if FromPid /= TCGL ->
IoFun = create_io_fun(FromPid, CtLogFd, EscChars),
IoList = lists:foldl(IoFun, [], Content),
- io:format(TCGL,["$tc_html","~ts"], [IoList]);
+ try io:format(TCGL,["$tc_html","~ts"], [IoList]) of
+ ok -> ok
+ catch
+ _:_ ->
+ io:format(TCGL,"~ts", [IoList])
+ end;
true ->
unexpected_io(FromPid, Category, ?MAX_IMPORTANCE, Content,
CtLogFd, EscChars)
@@ -958,7 +969,10 @@ print_to_log(async, FromPid, Category, TCGL, Content, EscChars, State) ->
_:terminated ->
unexpected_io(FromPid, Category,
?MAX_IMPORTANCE,
- Content, CtLogFd, EscChars)
+ Content, CtLogFd, EscChars);
+ _:_ ->
+ io:format(TCGL, "~ts",
+ [lists:foldl(IoFun,[],Content)])
end;
false ->
unexpected_io(FromPid, Category,
@@ -1099,26 +1113,27 @@ open_ctlog(MiscIoName) ->
"View I/O logged after the test run</a></li>\n</ul>\n",
[MiscIoName,MiscIoName]),
- print_style(Fd,undefined),
+ print_style(Fd,group_leader(),undefined),
io:format(Fd,
xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
"<br />\n<h4>PROGRESS LOG</h4>\n<pre>\n"), []),
Fd.
-print_style(Fd,undefined) ->
+print_style(Fd,GL,undefined) ->
case basic_html() of
true ->
- io:format(Fd,
- "<style>\n"
- "div.ct_internal { background:lightgrey; color:black; }\n"
- "div.default { background:lightgreen; color:black; }\n"
- "</style>\n",
- []);
+ Style = "<style>\n
+ div.ct_internal { background:lightgrey; color:black; }\n
+ div.default { background:lightgreen; color:black; }\n
+ </style>\n",
+ if Fd == GL -> io:format(["$tc_html",Style], []);
+ true -> io:format(Fd, Style, [])
+ end;
_ ->
ok
end;
-print_style(Fd,StyleSheet) ->
+print_style(Fd,GL,StyleSheet) ->
case file:read_file(StyleSheet) of
{ok,Bin} ->
Str = b2s(Bin,encoding(StyleSheet)),
@@ -1131,23 +1146,30 @@ print_style(Fd,StyleSheet) ->
N1 -> N1
end,
if (Pos0 == 0) and (Pos1 /= 0) ->
- print_style_error(Fd,StyleSheet,missing_style_start_tag);
+ print_style_error(Fd,GL,StyleSheet,missing_style_start_tag);
(Pos0 /= 0) and (Pos1 == 0) ->
- print_style_error(Fd,StyleSheet,missing_style_end_tag);
+ print_style_error(Fd,GL,StyleSheet,missing_style_end_tag);
Pos0 /= 0 ->
Style = string:sub_string(Str,Pos0,Pos1+7),
- io:format(Fd,"~ts\n",[Style]);
+ if Fd == GL -> io:format(Fd,["$tc_html","~ts\n"],[Style]);
+ true -> io:format(Fd,"~ts\n",[Style])
+ end;
Pos0 == 0 ->
- io:format(Fd,"<style>~ts</style>\n",[Str])
+ if Fd == GL -> io:format(Fd,["$tc_html","<style>\n~ts</style>\n"],[Str]);
+ true -> io:format(Fd,"<style>\n~ts</style>\n",[Str])
+ end
end;
{error,Reason} ->
- print_style_error(Fd,StyleSheet,Reason)
+ print_style_error(Fd,GL,StyleSheet,Reason)
end.
-print_style_error(Fd,StyleSheet,Reason) ->
- io:format(Fd,"\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
- [StyleSheet,Reason]),
- print_style(Fd,undefined).
+print_style_error(Fd,GL,StyleSheet,Reason) ->
+ IO = io_lib:format("\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
+ [StyleSheet,Reason]),
+ if Fd == GL -> io:format(Fd,["$tc_html",IO],[]);
+ true -> io:format(Fd,IO,[])
+ end,
+ print_style(Fd,GL,undefined).
close_ctlog(Fd) ->
io:format(Fd, "\n</pre>\n", []),
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 32c8ab96ec..c4905b316f 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -27,7 +27,7 @@
-export([run_on_node/2,run_on_node/3]).
-export([run_test/1,run_test/2]).
-export([get_event_mgr_ref/0]).
--export([basic_html/1]).
+-export([basic_html/1,esc_chars/1]).
-export([abort/0,abort/1,progress/0]).
@@ -317,6 +317,16 @@ basic_html(Bool) ->
ok.
%%%-----------------------------------------------------------------
+%%% @spec esc_chars(Bool) -> ok
+%%% Bool = true | false
+%%%
+%%% @doc If set to false, the ct_master logs will be written without
+%%% special characters being escaped in the HTML logs.
+esc_chars(Bool) ->
+ application:set_env(common_test_master, esc_chars, Bool),
+ ok.
+
+%%%-----------------------------------------------------------------
%%% MASTER, runs on central controlling node.
%%%-----------------------------------------------------------------
start_master(NodeOptsList) ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index e156c9b773..1e5f935198 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -65,6 +65,7 @@
logdir,
logopts = [],
basic_html,
+ esc_chars = true,
verbosity = [],
config = [],
event_handlers = [],
@@ -346,6 +347,15 @@ script_start1(Parent, Args) ->
application:set_env(common_test, basic_html, true),
true
end,
+ %% esc_chars - used by ct_logs
+ EscChars = case proplists:get_value(no_esc_chars, Args) of
+ undefined ->
+ application:set_env(common_test, esc_chars, true),
+ undefined;
+ _ ->
+ application:set_env(common_test, esc_chars, false),
+ false
+ end,
%% disable_log_cache - used by ct_logs
case proplists:get_value(disable_log_cache, Args) of
undefined ->
@@ -359,6 +369,7 @@ script_start1(Parent, Args) ->
cover = Cover, cover_stop = CoverStop,
logdir = LogDir, logopts = LogOpts,
basic_html = BasicHtml,
+ esc_chars = EscChars,
verbosity = Verbosity,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
@@ -587,6 +598,17 @@ combine_test_opts(TS, Specs, Opts) ->
BHBool
end,
+ EscChars =
+ case choose_val(Opts#opts.esc_chars,
+ TSOpts#opts.esc_chars) of
+ undefined ->
+ true;
+ ECBool ->
+ application:set_env(common_test, esc_chars,
+ ECBool),
+ ECBool
+ end,
+
Opts#opts{label = Label,
profile = Profile,
testspec_files = Specs,
@@ -595,6 +617,7 @@ combine_test_opts(TS, Specs, Opts) ->
logdir = which(logdir, LogDir),
logopts = AllLogOpts,
basic_html = BasicHtml,
+ esc_chars = EscChars,
verbosity = AllVerbosity,
silent_connections = AllSilentConns,
config = TSOpts#opts.config,
@@ -795,6 +818,7 @@ script_usage() ->
"\n\t [-scale_timetraps]"
"\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t [-basic_html]"
+ "\n\t [-no_esc_chars]"
"\n\t [-repeat N] |"
"\n\t [-duration HHMMSS [-force_stop [skip_rest]]] |"
"\n\t [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]"
@@ -822,6 +846,7 @@ script_usage() ->
"\n\t [-scale_timetraps]"
"\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t [-basic_html]"
+ "\n\t [-no_esc_chars]"
"\n\t [-repeat N] |"
"\n\t [-duration HHMMSS [-force_stop [skip_rest]]] |"
"\n\t [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]\n\n"),
@@ -847,7 +872,8 @@ script_usage() ->
"\n\t [-multiply_timetraps N]"
"\n\t [-scale_timetraps]"
"\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
- "\n\t [-basic_html]\n\n").
+ "\n\t [-basic_html]"
+ "\n\t [-no_esc_chars]\n\n").
%%%-----------------------------------------------------------------
%%% @hidden
@@ -1089,7 +1115,17 @@ run_test2(StartOpts) ->
application:set_env(common_test, basic_html, BasicHtmlBool),
BasicHtmlBool
end,
-
+ %% esc_chars - used by ct_logs
+ EscChars =
+ case proplists:get_value(esc_chars, StartOpts) of
+ undefined ->
+ application:set_env(common_test, esc_chars, true),
+ undefined;
+ EscCharsBool ->
+ application:set_env(common_test, esc_chars, EscCharsBool),
+ EscCharsBool
+ end,
+ %% disable_log_cache - used by ct_logs
case proplists:get_value(disable_log_cache, StartOpts) of
undefined ->
application:set_env(common_test, disable_log_cache, false);
@@ -1104,6 +1140,7 @@ run_test2(StartOpts) ->
cover = Cover, cover_stop = CoverStop,
step = Step, logdir = LogDir,
logopts = LogOpts, basic_html = BasicHtml,
+ esc_chars = EscChars,
config = CfgFiles,
verbosity = Verbosity,
event_handlers = EvHandlers,
@@ -1445,6 +1482,7 @@ get_data_for_node(#testspec{label = Labels,
logdir = LogDirs,
logopts = LogOptsList,
basic_html = BHs,
+ esc_chars = EscChs,
stylesheet = SSs,
verbosity = VLvls,
silent_connections = SilentConnsList,
@@ -1472,6 +1510,7 @@ get_data_for_node(#testspec{label = Labels,
LOs -> LOs
end,
BasicHtml = proplists:get_value(Node, BHs),
+ EscChars = proplists:get_value(Node, EscChs),
Stylesheet = proplists:get_value(Node, SSs),
Verbosity = case proplists:get_value(Node, VLvls) of
undefined -> [];
@@ -1498,6 +1537,7 @@ get_data_for_node(#testspec{label = Labels,
logdir = LogDir,
logopts = LogOpts,
basic_html = BasicHtml,
+ esc_chars = EscChars,
stylesheet = Stylesheet,
verbosity = Verbosity,
silent_connections = SilentConns,
@@ -2182,10 +2222,18 @@ do_run_test(Tests, Skip, Opts0) ->
%% test_server needs to know the include path too
InclPath = case application:get_env(common_test, include) of
{ok,Incls} -> Incls;
- _ -> []
+ _ -> []
end,
application:set_env(test_server, include, InclPath),
+ %% copy the escape characters setting to test_server
+ EscChars =
+ case application:get_env(common_test, esc_chars) of
+ {ok,ECBool} -> ECBool;
+ _ -> true
+ end,
+ application:set_env(test_server, esc_chars, EscChars),
+
test_server_ctrl:start_link(local),
%% let test_server expand the test tuples and count no of cases
@@ -3069,6 +3117,10 @@ opts2args(EnvStartOpts) ->
[{basic_html,[]}];
({basic_html,false}) ->
[];
+ ({esc_chars,false}) ->
+ [{no_esc_chars,[]}];
+ ({esc_chars,true}) ->
+ [];
({event_handler,EH}) when is_atom(EH) ->
[{event_handler,[atom_to_list(EH)]}];
({event_handler,EHs}) when is_list(EHs) ->
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 4d3fd2d094..f5f4f648f4 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -42,7 +42,8 @@
%% {reconnection_interval,Millisec},
%% {keep_alive,Bool},
%% {poll_limit,N},
-%% {poll_interval,Millisec}]}.</pre>
+%% {poll_interval,Millisec},
+%% {tcp_nodelay,Bool}]}.</pre>
%% <p><code>Millisec = integer(), N = integer()</code></p>
%% <p>Enter the <code>telnet_settings</code> term in a configuration
%% file included in the test and ct_telnet will retrieve the information
@@ -182,7 +183,8 @@
conn_to=?DEFAULT_TIMEOUT,
com_to=?DEFAULT_TIMEOUT,
reconns=?RECONNS,
- reconn_int=?RECONN_TIMEOUT}).
+ reconn_int=?RECONN_TIMEOUT,
+ tcp_nodelay=false}).
%%%-----------------------------------------------------------------
%%% @spec open(Name) -> {ok,Handle} | {error,Reason}
@@ -602,8 +604,18 @@ init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) ->
Settings ->
set_telnet_defaults(Settings,#state{})
end,
- case catch TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
- KeepAlive,Extra) of
+ %% Handle old user versions of TargetMod
+ code:ensure_loaded(TargetMod),
+ try
+ case erlang:function_exported(TargetMod,connect,7) of
+ true ->
+ TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
+ KeepAlive,S0#state.tcp_nodelay,Extra);
+ false ->
+ TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
+ KeepAlive,Extra)
+ end
+ of
{ok,TelnPid} ->
put({ct_telnet_pid2name,TelnPid},Name),
S1 = S0#state{host=Ip,
@@ -625,15 +637,18 @@ init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) ->
"Connection timeout: ~p\n"
"Keep alive: ~w\n"
"Poll limit: ~w\n"
- "Poll interval: ~w",
+ "Poll interval: ~w\n"
+ "TCP nodelay: ~w",
[Ip,Port,S1#state.com_to,S1#state.reconns,
S1#state.reconn_int,S1#state.conn_to,KeepAlive,
- S1#state.poll_limit,S1#state.poll_interval]),
+ S1#state.poll_limit,S1#state.poll_interval,
+ S1#state.tcp_nodelay]),
{ok,TelnPid,S1};
- {'EXIT',Reason} ->
- {error,Reason};
Error ->
Error
+ catch
+ _:Reason ->
+ {error,Reason}
end.
type(telnet) -> ip;
@@ -653,6 +668,8 @@ set_telnet_defaults([{poll_limit,PL}|Ss],S) ->
set_telnet_defaults(Ss,S#state{poll_limit=PL});
set_telnet_defaults([{poll_interval,PI}|Ss],S) ->
set_telnet_defaults(Ss,S#state{poll_interval=PI});
+set_telnet_defaults([{tcp_nodelay,NoDelay}|Ss],S) ->
+ set_telnet_defaults(Ss,S#state{tcp_nodelay=NoDelay});
set_telnet_defaults([Unknown|Ss],S) ->
force_log(S,error,
"Bad element in telnet_settings: ~p",[Unknown]),
@@ -794,8 +811,17 @@ reconnect(Ip,Port,N,State=#state{name=Name,
keep_alive=KeepAlive,
extra=Extra,
conn_to=ConnTo,
- reconn_int=ReconnInt}) ->
- case TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,Extra) of
+ reconn_int=ReconnInt,
+ tcp_nodelay=NoDelay}) ->
+ %% Handle old user versions of TargetMod
+ ConnResult =
+ case erlang:function_exported(TargetMod,connect,7) of
+ true ->
+ TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,NoDelay,Extra);
+ false ->
+ TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,Extra)
+ end,
+ case ConnResult of
{ok,NewPid} ->
put({ct_telnet_pid2name,NewPid},Name),
{ok, NewPid, State#state{teln_pid=NewPid}};
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index f1dee1f3bb..1f1311776f 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -35,7 +35,7 @@
%%-define(debug, true).
--export([open/2, open/3, open/4, open/5, close/1]).
+-export([open/2, open/3, open/4, open/5, open/6, close/1]).
-export([send_data/2, send_data/3, get_data/1]).
-define(TELNET_PORT, 23).
@@ -70,19 +70,22 @@
-record(state,{conn_name, get_data, keep_alive=true, log_pos=1}).
open(Server, ConnName) ->
- open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true, ConnName).
+ open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true, false, ConnName).
open(Server, Port, ConnName) ->
- open(Server, Port, ?OPEN_TIMEOUT, true, ConnName).
+ open(Server, Port, ?OPEN_TIMEOUT, true, false, ConnName).
open(Server, Port, Timeout, ConnName) ->
- open(Server, Port, Timeout, true, ConnName).
+ open(Server, Port, Timeout, true, false, ConnName).
open(Server, Port, Timeout, KeepAlive, ConnName) ->
+ open(Server, Port, Timeout, KeepAlive, false, ConnName).
+
+open(Server, Port, Timeout, KeepAlive, NoDelay, ConnName) ->
Self = self(),
Pid = spawn(fun() ->
init(Self, Server, Port, Timeout,
- KeepAlive, ConnName)
+ KeepAlive, NoDelay, ConnName)
end),
receive
{open,Pid} ->
@@ -114,8 +117,8 @@ get_data(Pid) ->
%%%-----------------------------------------------------------------
%%% Internal functions
-init(Parent, Server, Port, Timeout, KeepAlive, ConnName) ->
- case gen_tcp:connect(Server, Port, [list,{packet,0},{nodelay,true}], Timeout) of
+init(Parent, Server, Port, Timeout, KeepAlive, NoDelay, ConnName) ->
+ case gen_tcp:connect(Server, Port, [list,{packet,0},{nodelay,NoDelay}], Timeout) of
{ok,Sock} ->
dbg("~p connected to: ~p (port: ~w, keep_alive: ~w)\n",
[ConnName,Server,Port,KeepAlive]),
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index df5587b069..991abb0666 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1146,8 +1146,9 @@ should_be_added(Tag,Node,_Data,Spec) ->
if
%% list terms *without* possible duplicates here
Tag == logdir; Tag == logopts;
- Tag == basic_html; Tag == label;
- Tag == auto_compile; Tag == abort_if_missing_suites;
+ Tag == basic_html; Tag == esc_chars;
+ Tag == label; Tag == auto_compile;
+ Tag == abort_if_missing_suites;
Tag == stylesheet; Tag == verbosity;
Tag == silent_connections ->
lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
@@ -1544,6 +1545,8 @@ valid_terms() ->
{logopts,3},
{basic_html,2},
{basic_html,3},
+ {esc_chars,2},
+ {esc_chars,3},
{verbosity,2},
{verbosity,3},
{silent_connections,2},
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index c372763ae9..d5a8e3fbc0 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -459,6 +459,7 @@ loop(Mode,TestData,StartDir) ->
error:badarg -> []
end,
ct_hooks:terminate(Callbacks),
+
close_connections(ets:tab2list(?conn_table)),
ets:delete(?conn_table),
ets:delete(?board_table),
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index c1cc1eafbd..d7efa26863 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -37,6 +37,7 @@
logdir=["."],
logopts=[],
basic_html=[],
+ esc_chars=[],
verbosity=[],
silent_connections=[],
cover=[],
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index e762d5060f..33a3813a16 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -130,7 +130,14 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
tag_event(Event)),
if is_list(SReport) ->
SaslHeader = format_header(State),
- ct_logs:LogFunc(sasl, ?STD_IMPORTANCE, SaslHeader, SReport, []);
+ case LogFunc of
+ tc_log ->
+ ct_logs:tc_log(sasl, ?STD_IMPORTANCE,
+ SaslHeader, SReport, [], []);
+ tc_log_async ->
+ ct_logs:tc_log_async(sasl, ?STD_IMPORTANCE,
+ SaslHeader, SReport, [])
+ end;
true -> %% Report is an atom if no logging is to be done
ignore
end
@@ -139,7 +146,14 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
tag_event(Event),io_lib),
if is_list(EReport) ->
ErrHeader = format_header(State),
- ct_logs:LogFunc(error_logger, ?STD_IMPORTANCE, ErrHeader, EReport, []);
+ case LogFunc of
+ tc_log ->
+ ct_logs:tc_log(error_logger, ?STD_IMPORTANCE,
+ ErrHeader, EReport, [], []);
+ tc_log_async ->
+ ct_logs:tc_log_async(error_logger, ?STD_IMPORTANCE,
+ ErrHeader, EReport, [])
+ end;
true -> %% Report is an atom if no logging is to be done
ignore
end,
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index 74a97fe2e2..59b916851e 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -82,7 +82,8 @@ init(Path, Opts) ->
url_base = proplists:get_value(url_base,Opts),
timer = ?now }.
-pre_init_per_suite(Suite,SkipOrFail,State) when is_tuple(SkipOrFail) ->
+pre_init_per_suite(Suite,SkipOrFail,#state{ test_cases = [] } = State)
+ when is_tuple(SkipOrFail) ->
{SkipOrFail, init_tc(State#state{curr_suite = Suite,
curr_suite_ts = ?now},
SkipOrFail) };
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index 34acad6fd1..919526c5d7 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -2165,10 +2165,19 @@ get_timetrap_info(TCPid, SendToServer) ->
Timers ->
case [Info || {Handle,Pid,Info} <- Timers,
Pid == TCPid, Handle /= infinity] of
- [I|_] ->
- I;
+ [{TVal,true}|_] ->
+ {TVal,{true,test_server:timetrap_scale_factor()}};
+ [{TVal,false}|_] ->
+ {TVal,{false,1}};
[] when SendToServer == true ->
- tc_supervisor_req({get_timetrap_info,TCPid});
+ case tc_supervisor_req({get_timetrap_info,TCPid}) of
+ {TVal,true} ->
+ {TVal,{true,test_server:timetrap_scale_factor()}};
+ {TVal,false} ->
+ {TVal,{false,1}};
+ Error ->
+ Error
+ end;
[] ->
undefined
end
diff --git a/lib/common_test/src/test_server_gl.erl b/lib/common_test/src/test_server_gl.erl
index c150570604..333c8fc06e 100644
--- a/lib/common_test/src/test_server_gl.erl
+++ b/lib/common_test/src/test_server_gl.erl
@@ -131,6 +131,10 @@ set_props(GL, PropList) ->
%%% Internal functions.
init([]) ->
+ EscChars = case application:get_env(test_server, esc_chars) of
+ {ok,ECBool} -> ECBool;
+ _ -> true
+ end,
{ok,#st{tc_supervisor=none,
minor=none,
minor_monitor=none,
@@ -139,7 +143,7 @@ init([]) ->
permit_io=gb_sets:empty(),
auto_nl=true,
levels={1,19,10},
- escape_chars=true
+ escape_chars=EscChars
}}.
req(GL, Req) ->
diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl
index cd493d293f..4897ddb2f8 100644
--- a/lib/common_test/src/unix_telnet.erl
+++ b/lib/common_test/src/unix_telnet.erl
@@ -27,7 +27,8 @@
%%% {port,PortNum}, % optional
%%% {username,UserName},
%%% {password,Password},
-%%% {keep_alive,Bool}]}. % optional</pre>
+%%% {keep_alive,Bool}, % optional
+%%% {tcp_nodely,Bool}]} % optional</pre>
%%%
%%% <p>To communicate via telnet to the host specified by
%%% <code>HostNameOrIpAddress</code>, use the interface functions in
@@ -55,7 +56,7 @@
-compile(export_all).
%% Callbacks for ct_telnet.erl
--export([connect/6,get_prompt_regexp/0]).
+-export([connect/7,get_prompt_regexp/0]).
-import(ct_telnet,[start_gen_log/1,log/4,end_gen_log/0]).
-define(username,"login: ").
@@ -82,6 +83,7 @@ get_prompt_regexp() ->
%%% Port = integer()
%%% Timeout = integer()
%%% KeepAlive = bool()
+%%% TCPNoDelay = bool()
%%% Extra = ct:target_name() | {Username,Password}
%%% Username = string()
%%% Password = string()
@@ -91,25 +93,25 @@ get_prompt_regexp() ->
%%% @doc Callback for ct_telnet.erl.
%%%
%%% <p>Setup telnet connection to a unix host.</p>
-connect(ConnName,Ip,Port,Timeout,KeepAlive,Extra) ->
+connect(ConnName,Ip,Port,Timeout,KeepAlive,TCPNoDelay,Extra) ->
case Extra of
{Username,Password} ->
- connect1(ConnName,Ip,Port,Timeout,KeepAlive,
+ connect1(ConnName,Ip,Port,Timeout,KeepAlive,TCPNoDelay,
Username,Password);
KeyOrName ->
case get_username_and_password(KeyOrName) of
{ok,{Username,Password}} ->
- connect1(ConnName,Ip,Port,Timeout,KeepAlive,
+ connect1(ConnName,Ip,Port,Timeout,KeepAlive,TCPNoDelay,
Username,Password);
Error ->
Error
end
end.
-connect1(Name,Ip,Port,Timeout,KeepAlive,Username,Password) ->
+connect1(Name,Ip,Port,Timeout,KeepAlive,TCPNoDelay,Username,Password) ->
start_gen_log("unix_telnet connect"),
Result =
- case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive,Name) of
+ case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive,TCPNoDelay,Name) of
{ok,Pid} ->
case ct_telnet:silent_teln_expect(Name,Pid,[],
[prompt],?prx,[]) of
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
index a281ddc938..20f139bcc8 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
@@ -1,8 +1,8 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
-%%
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
@@ -14,46 +14,46 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(ct_update_config_SUITE).
-
--suite_defaults([{timetrap, {minutes, 10}}]).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include("ct.hrl").
-
--define(now, os:timestamp()).
-
-%% Test server callback functions
-init_per_suite(Config) ->
- [{init_per_suite,?now}|Config].
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_testcase(_TestCase, Config) ->
- [{init_per_testcase,?now}|Config].
-
-end_per_testcase(_TestCase, _Config) ->
- ok.
-
-init_per_group(GroupName, Config) ->
- [{init_per_group,?now}|Config].
-
-end_per_group(GroupName, Config) ->
- ok.
-
-all() ->
- [{group,group1}].
-
-groups() ->
- [{group1,[],[test_case]}].
-
-%% Test cases starts here.
-test_case(Config) when is_list(Config) ->
- ok.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_update_config_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+-define(now, ct_test_support:unique_timestamp()).
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{init_per_suite,?now}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ [{init_per_testcase,?now}|Config].
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(GroupName, Config) ->
+ [{init_per_group,?now}|Config].
+
+end_per_group(GroupName, Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
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 dc0056b61b..c00eb5cf93 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
@@ -90,7 +90,7 @@ id(Opts) ->
gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, id, [Opts]}}),
ct:log("~w:id called", [?MODULE]),
- os:timestamp().
+ ct_test_support:unique_timestamp().
%% @doc Called before init_per_suite is called. Note that this callback is
%% only called if the CTH is added before init_per_suite is run (eg. in a test
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
index 02f7fa7564..d48981f667 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
@@ -25,7 +25,7 @@
-include_lib("common_test/src/ct_util.hrl").
-include_lib("common_test/include/ct_event.hrl").
--define(now, os:timestamp()).
+-define(now, ct_test_support:unique_timestamp()).
%% CT Hooks
-compile(export_all).
diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl
index 72c1de4023..03fbc17bd2 100644
--- a/lib/common_test/test/ct_netconfc_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE.erl
@@ -48,7 +48,12 @@ init_per_suite(Config) ->
{error,Reason} when Reason=/={already_loaded,crypto} ->
{skip, Reason};
_ ->
- ct_test_support:init_per_suite(Config)
+ case application:load(ssh) of
+ {error,Reason} when Reason=/={already_loaded,ssh} ->
+ {skip, Reason};
+ _ ->
+ ct_test_support:init_per_suite(Config)
+ end
end.
end_per_suite(Config) ->
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
index e4062a7d62..065639dd36 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -123,15 +123,21 @@ init_per_testcase(_Case, Config) ->
end_per_testcase(_Case, _Config) ->
ok.
+init_per_suite() ->
+ [{timetrap,2*?default_timeout}]. % making dsa files can be slow
init_per_suite(Config) ->
- case catch {crypto:start(), ssh:start()} of
- {ok, ok} ->
+ case catch ssh:start() of
+ Ok when Ok==ok; Ok=={error,{already_started,ssh}} ->
+ ct:log("ssh started",[]),
{ok, _} = netconfc_test_lib:get_id_keys(Config),
netconfc_test_lib:make_dsa_files(Config),
+ ct:log("dsa files created",[]),
Server = ?NS:start(?config(data_dir,Config)),
+ ct:log("netconf server started",[]),
[{server,Server}|Config];
- _ ->
- {skip, "Crypto and/or SSH could not be started!"}
+ Other ->
+ ct:log("could not start ssh: ~p",[Other]),
+ {skip, "SSH could not be started!"}
end.
end_per_suite(Config) ->
@@ -806,7 +812,7 @@ close_while_waiting_for_chunked_data(Config) ->
%% Order server to expect a get - then the process above will make
%% sure the rpc-reply is sent - but only a part of it - then close.
?NS:expect('get'),
- {error,closed} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000),
+ {error,closed} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},4000),
ok.
connection_crash(Config) ->
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
index ae9087ce45..04bfe75187 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
@@ -26,7 +26,8 @@
-compile(export_all).
suite() ->
- [{ct_hooks, [{cth_conn_log,[{ct_netconfc,[{log_type,html}]}]}]}].
+ [{timetrap,?default_timeout},
+ {ct_hooks, [{cth_conn_log,[{ct_netconfc,[{log_type,html}]}]}]}].
all() ->
case os:find_executable("ssh") of
@@ -48,13 +49,10 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(Case, Config) ->
stop_node(Case),
- Dog = test_server:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
+ Config.
end_per_testcase(Case, Config) ->
stop_node(Case),
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
ok.
stop_node(Case) ->
@@ -63,14 +61,19 @@ stop_node(Case) ->
rpc:call(Node,erlang,halt,[]).
+init_per_suite() ->
+ [{timetrap,2*?default_timeout}]. % making dsa files can be slow
init_per_suite(Config) ->
- case {crypto:start(),ssh:start()} of
- {ok,ok} ->
+ case ssh:start() of
+ Ok when Ok==ok; Ok=={error,{already_started,ssh}} ->
+ ct:log("SSH started locally",[]),
{ok, _} = netconfc_test_lib:get_id_keys(Config),
netconfc_test_lib:make_dsa_files(Config),
+ ct:log("dsa files created",[]),
Config;
- _ ->
- {skip, "Crypto and/or SSH could not be started locally!"}
+ Other ->
+ ct:log("could not start ssh locally: ~p",[Other]),
+ {skip, "SSH could not be started locally!"}
end.
end_per_suite(Config) ->
@@ -87,12 +90,15 @@ remote_crash(Config) ->
Pa = filename:dirname(code:which(?NS)),
true = rpc:call(Node,code,add_patha,[Pa]),
- case {rpc:call(Node,crypto,start,[]),rpc:call(Node,ssh,start,[])} of
- {ok,ok} ->
+ case rpc:call(Node,ssh,start,[]) of
+ Ok when Ok==ok; Ok=={error,{already_started,ssh}} ->
+ ct:log("SSH started remote",[]),
Server = rpc:call(Node,?NS,start,[?config(data_dir,Config)]),
+ ct:log("netconf server started remote",[]),
remote_crash(Node,Config);
- _ ->
- {skip, "Crypto and/or SSH could not be started remote!"}
+ Other ->
+ ct:log("could not start ssh remote: ~p",[Other]),
+ {skip, "SSH could not be started remote!"}
end.
remote_crash(Node,Config) ->
diff --git a/lib/common_test/test/ct_pre_post_test_io_SUITE.erl b/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
index 74ef3a26ce..7ffe6f045b 100644
--- a/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
+++ b/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
@@ -44,13 +44,29 @@
%% instance, the tests need to be performed on a separate node (or
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,120}}].
+
+all() ->
+ [
+ pre_post_io
+ ].
+
init_per_suite(Config) ->
- DataDir = ?config(data_dir, Config),
- CTH = filename:join(DataDir, "cth_ctrl.erl"),
- ct:pal("Compiling ~p: ~p",
- [CTH,compile:file(CTH,[{outdir,DataDir},debug_info])]),
- ct_test_support:init_per_suite([{path_dirs,[DataDir]},
- {start_sasl,true} | Config]).
+ TTInfo = {_T,{_Scaled,ScaleVal}} = ct:get_timetrap_info(),
+ ct:pal("Timetrap info = ~w", [TTInfo]),
+ if ScaleVal > 1 ->
+ {skip,"Skip on systems running e.g. cover or debug!"};
+ ScaleVal =< 1 ->
+ DataDir = ?config(data_dir, Config),
+ CTH = filename:join(DataDir, "cth_ctrl.erl"),
+ ct:pal("Compiling ~p: ~p",
+ [CTH,compile:file(CTH,[{outdir,DataDir},
+ debug_info])]),
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]},
+ {start_sasl,true} | Config])
+ end.
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
@@ -61,13 +77,6 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [
- pre_post_io
- ].
-
%%--------------------------------------------------------------------
%% TEST CASES
%%--------------------------------------------------------------------
@@ -90,31 +99,50 @@ pre_post_io(Config) ->
%%!--------------------------------------------------------------------
spawn(fun() ->
- ct:pal("CONTROLLER: Started!", []),
+ ct:pal("CONTROLLER: Starting test run #1...", []),
%% --- test run 1 ---
- ct:sleep(3000),
- ct:pal("CONTROLLER: Handle remote events = true", []),
- ok = ct_test_support:ct_rpc({cth_log_redirect,
- handle_remote_events,
- [true]}, Config),
- ct:sleep(2000),
- ct:pal("CONTROLLER: Proceeding with test run #1!", []),
+ try_loop(ct_test_support, ct_rpc, [{cth_log_redirect,
+ handle_remote_events,
+ [true]}, Config], 3000),
+ CTLoggerPid1 = ct_test_support:ct_rpc({erlang,whereis,
+ [ct_logs]}, Config),
+ ct:pal("CONTROLLER: Logger = ~w~nHandle remote events = true",
+ [CTLoggerPid1]),
+ ct:sleep(5000),
+ ct:pal("CONTROLLER: Proceeding with test run #1...", []),
ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
ct:sleep(6000),
- ct:pal("CONTROLLER: Proceeding with shutdown #1!", []),
+ ct:pal("CONTROLLER: Proceeding with shutdown #1...", []),
ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
+ try_loop(fun() ->
+ false = ct_test_support:ct_rpc({erlang,
+ is_process_alive,
+ [CTLoggerPid1]},
+ Config)
+ end, 3000),
+ ct:pal("CONTROLLER: Shutdown #1 complete!", []),
+ ct:pal("CONTROLLER: Starting test run #2...", []),
%% --- test run 2 ---
- ct:sleep(3000),
- ct:pal("CONTROLLER: Handle remote events = true", []),
- ok = ct_test_support:ct_rpc({cth_log_redirect,
- handle_remote_events,
- [true]}, Config),
- ct:sleep(2000),
- ct:pal("CONTROLLER: Proceeding with test run #2!", []),
+ try_loop(ct_test_support, ct_rpc, [{cth_log_redirect,
+ handle_remote_events,
+ [true]}, Config], 3000),
+ CTLoggerPid2 = ct_test_support:ct_rpc({erlang,whereis,
+ [ct_logs]}, Config),
+ ct:pal("CONTROLLER: Logger = ~w~nHandle remote events = true",
+ [CTLoggerPid2]),
+ ct:sleep(5000),
+ ct:pal("CONTROLLER: Proceeding with test run #2...", []),
ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
ct:sleep(6000),
- ct:pal("CONTROLLER: Proceeding with shutdown #2!", []),
- ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config)
+ ct:pal("CONTROLLER: Proceeding with shutdown #2...", []),
+ ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
+ try_loop(fun() ->
+ false = ct_test_support:ct_rpc({erlang,
+ is_process_alive,
+ [CTLoggerPid2]},
+ Config)
+ end, 3000),
+ ct:pal("CONTROLLER: Shutdown #2 complete!", [])
end),
ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -157,7 +185,7 @@ pre_post_io(Config) ->
Counters
end, {pre,0,0,0,0}, Ts),
[_|Counters] = tuple_to_list(PrePostIOEntries),
- ct:log("Entries in the Pre/Post Test IO Log: ~p", [Counters]),
+ ct:pal("Entries in the Pre/Post Test IO Log: ~w", [Counters]),
case [C || C <- Counters, C < 2] of
[] ->
ok;
@@ -183,7 +211,7 @@ pre_post_io(Config) ->
[LogN,ErrN+1];
(_, Counters) -> Counters
end, [0,0], Ts),
- ct:log("Entries in the Unexpected IO Log: ~p", [UnexpIOEntries]),
+ ct:log("Entries in the Unexpected IO Log: ~w", [UnexpIOEntries]),
case [N || N <- UnexpIOEntries, N < 2] of
[] ->
ok;
@@ -208,6 +236,38 @@ setup(Test, Config) ->
reformat(Events, EH) ->
ct_test_support:reformat(Events, EH).
+try_loop(_Fun, 0) ->
+ ct:pal("WARNING! Fun never succeeded!", []),
+ gave_up;
+try_loop(Fun, N) ->
+ try Fun() of
+ {error,_} ->
+ timer:sleep(10),
+ try_loop(Fun, N-1);
+ Result ->
+ Result
+ catch
+ _:_What ->
+ timer:sleep(10),
+ try_loop(Fun, N-1)
+ end.
+
+try_loop(M, F, _A, 0) ->
+ ct:pal("WARNING! ~w:~w never succeeded!", [M,F]),
+ gave_up;
+try_loop(M, F, A, N) ->
+ try apply(M, F, A) of
+ {error,_} ->
+ timer:sleep(10),
+ try_loop(M, F, A, N-1);
+ Result ->
+ Result
+ catch
+ _:_ ->
+ timer:sleep(10),
+ try_loop(M, F, A, N-1)
+ end.
+
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl b/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
index cb0c5ed14e..347b507c78 100644
--- a/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
+++ b/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
@@ -53,8 +53,7 @@ init(_Id, _Opts) ->
receive
{?MODULE,proceed} -> ok
after
- 10000 ->
- ok
+ 10000 -> ok
end,
{ok,[],ct_last}.
@@ -66,8 +65,7 @@ terminate(_State) ->
receive
{?MODULE,proceed} -> ok
after
- 10000 ->
- ok
+ 10000 -> ok
end,
stop_external_logger(cth_logger),
stop_dispatcher(),
@@ -94,7 +92,7 @@ init_logger(Name) ->
logger_loop(N) ->
ct:log("Logger iteration: ~p", [N]),
error_logger:error_report(N),
- timer:sleep(250),
+ timer:sleep(100),
logger_loop(N+1).
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl
index a39a3887f7..42ec685c16 100644
--- a/lib/common_test/test/ct_surefire_SUITE.erl
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -49,8 +49,11 @@
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- Config1 = ct_test_support:init_per_suite(Config),
- Config1.
+ DataDir = ?config(data_dir,Config),
+ Hook = "fail_pre_init_per_suite.erl",
+ io:format("Compiling ~p: ~p~n",
+ [Hook, compile:file(Hook,[{outdir,DataDir},debug_info])]),
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]}|Config]).
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
@@ -69,7 +72,8 @@ all() ->
absolute_path,
relative_path,
url,
- logdir
+ logdir,
+ fail_pre_init_per_suite
].
%%--------------------------------------------------------------------
@@ -107,6 +111,14 @@ logdir(Config) when is_list(Config) ->
Path = "logdir.xml",
run(logdir,[{cth_surefire,[{path,Path}]}],Path,Config,[{logdir,MyLogDir}]).
+fail_pre_init_per_suite(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Suites = [filename:join(DataDir,"pass_SUITE"),
+ filename:join(DataDir,"fail_SUITE")],
+ Path = "fail_pre_init_per_suite.xml",
+ run(fail_pre_init_per_suite,[fail_pre_init_per_suite,
+ {cth_surefire,[{path,Path}]}],Path,Config,[],Suites).
+
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -115,6 +127,8 @@ run(Case,CTHs,Report,Config) ->
run(Case,CTHs,Report,Config,ExtraOpts) ->
DataDir = ?config(data_dir, Config),
Suite = filename:join(DataDir, "surefire_SUITE"),
+ run(Case,CTHs,Report,Config,ExtraOpts,Suite).
+run(Case,CTHs,Report,Config,ExtraOpts,Suite) ->
{Opts,ERPid} = setup([{suite,Suite},{ct_hooks,CTHs},{label,Case}|ExtraOpts],
Config),
ok = execute(Case, Opts, ERPid, Config),
@@ -142,7 +156,6 @@ setup(Test, Config) ->
execute(Name, Opts, ERPid, Config) ->
ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
-
ct_test_support:log_events(Name,
reformat(Events, ?eh),
?config(priv_dir, Config),
@@ -166,10 +179,30 @@ events_to_check(_, 0) ->
events_to_check(Test, N) ->
test_events(Test) ++ events_to_check(Test, N-1).
-test_events(_) ->
- [{?eh,start_logging,'_'},
- {?eh,start_info,{1,1,9}},
- {?eh,tc_start,{surefire_SUITE,init_per_suite}},
+test_suite_events(fail_SUITE, TestStat) ->
+ [{?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,tc_done,{ct_framework,init_per_suite,
+ {failed,{error,pre_init_per_suite}}}},
+ {?eh,tc_auto_skip,
+ {fail_SUITE,test_case,
+ {failed,{ct_framework,init_per_suite,{failed,pre_init_per_suite}}}}},
+ {?eh,test_stats,TestStat},
+ {?eh,tc_auto_skip,
+ {ct_framework,end_per_suite,
+ {failed,{ct_framework,init_per_suite,{failed,pre_init_per_suite}}}}}].
+
+test_suite_events(fail_SUITE) ->
+ test_suite_events(fail_SUITE, {0,0,{0,1}});
+test_suite_events(pass_SUITE) ->
+ [{?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,tc_done,{ct_framework,init_per_suite,ok}},
+ {?eh,tc_start,{pass_SUITE,test_case}},
+ {?eh,tc_done,{pass_SUITE,test_case,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{ct_framework,end_per_suite}},
+ {?eh,tc_done,{ct_framework,end_per_suite,ok}}];
+test_suite_events(_) ->
+ [{?eh,tc_start,{surefire_SUITE,init_per_suite}},
{?eh,tc_done,{surefire_SUITE,init_per_suite,ok}},
{?eh,tc_start,{surefire_SUITE,tc_ok}},
{?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
@@ -216,9 +249,18 @@ test_events(_) ->
{surefire_SUITE,init_per_group,
{'EXIT',all_cases_should_be_skipped}}}}}],
{?eh,tc_start,{surefire_SUITE,end_per_suite}},
- {?eh,tc_done,{surefire_SUITE,end_per_suite,ok}},
- {?eh,stop_logging,[]}].
-
+ {?eh,tc_done,{surefire_SUITE,end_per_suite,ok}}].
+
+test_events(fail_pre_init_per_suite) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,start_info,{2,2,2}}] ++
+ test_suite_events(pass_SUITE) ++
+ test_suite_events(fail_SUITE, {1,0,{0,1}}) ++
+ [{?eh,stop_logging,[]}];
+test_events(Test) ->
+ [{?eh,start_logging,'_'}, {?eh,start_info,{1,1,9}}] ++
+ test_suite_events(Test) ++
+ [{?eh,stop_logging,[]}].
%%%-----------------------------------------------------------------
%%% Check generated xml log files
@@ -251,9 +293,9 @@ do_check_xml(Case,[Xml|Xmls]) ->
{E,_} = xmerl_scan:file(Xml),
Expected = events_to_result(lists:flatten(test_events(Case))),
ParseResult = testsuites(Case,E),
- ct:log("Expecting: ~p~n",[[Expected]]),
+ ct:log("Expecting: ~p~n",[Expected]),
ct:log("Actual : ~p~n",[ParseResult]),
- [Expected] = ParseResult,
+ Expected = ParseResult,
do_check_xml(Case,Xmls);
do_check_xml(_,[]) ->
ok.
@@ -265,7 +307,8 @@ testsuites(Case,#xmlElement{name=testsuites,content=TS}) ->
testsuite(Case,TS).
testsuite(Case,[#xmlElement{name=testsuite,content=TC,attributes=A}|TS]) ->
- {ET,EF,ES} = events_to_numbers(lists:flatten(test_events(Case))),
+ TestSuiteEvents = test_suite_events(get_ts_name(A)),
+ {ET,EF,ES} = events_to_numbers(lists:flatten(TestSuiteEvents)),
{T,E,F,S} = get_numbers_from_attrs(A,false,false,false,false),
ct:log("Expecting total:~p, error:~p, failure:~p, skipped:~p~n",[ET,0,EF,ES]),
ct:log("Actual total:~p, error:~p, failure:~p, skipped:~p~n",[T,E,F,S]),
@@ -318,14 +361,32 @@ failed_or_skipped([]) ->
%% Testsuites = [Testsuite]
%% Testsuite = [Testcase]
%% Testcase = [] | [f] | [s], indicating ok, failed and skipped respectively
-events_to_result([{?eh,tc_done,{_Suite,_Case,R}}|E]) ->
- [result(R)|events_to_result(E)];
-events_to_result([{?eh,tc_auto_skip,_}|E]) ->
- [[s]|events_to_result(E)];
-events_to_result([_|E]) ->
- events_to_result(E);
-events_to_result([]) ->
- [].
+events_to_result(E) ->
+ events_to_result(E, []).
+
+events_to_result([{?eh,tc_auto_skip,{_Suite,init_per_suite,_}}|E], Result) ->
+ {Suite,Rest} = events_to_result1(E),
+ events_to_result(Rest, [[[s]|Suite]|Result]);
+events_to_result([{?eh,tc_done,{_Suite,init_per_suite,R}}|E], Result) ->
+ {Suite,Rest} = events_to_result1(E),
+ events_to_result(Rest, [[result(R)|Suite]|Result]);
+events_to_result([_|E], Result) ->
+ events_to_result(E, Result);
+events_to_result([], Result) ->
+ Result.
+
+events_to_result1([{?eh,tc_auto_skip,{_Suite, end_per_suite,_}}|E]) ->
+ {[[s]],E};
+events_to_result1([{?eh,tc_done,{_Suite, end_per_suite,R}}|E]) ->
+ {[result(R)],E};
+events_to_result1([{?eh,tc_done,{_Suite,_Case,R}}|E]) ->
+ {Suite,Rest} = events_to_result1(E),
+ {[result(R)|Suite],Rest};
+events_to_result1([{?eh,tc_auto_skip,_}|E]) ->
+ {Suite,Rest} = events_to_result1(E),
+ {[[s]|Suite],Rest};
+events_to_result1([_|E]) ->
+ events_to_result1(E).
result(ok) ->[];
result({skipped,_}) -> [s];
@@ -374,3 +435,7 @@ del_files(Dir,[F0|Fs] ) ->
end;
del_files(_,[]) ->
ok.
+
+get_ts_name(Attributes) ->
+ {_,name,_,_,_,_,_,_,Name,_} = lists:keyfind(name, 2, Attributes),
+ list_to_atom(Name).
diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_1.erl b/lib/common_test/test/ct_surefire_SUITE_data/fail_SUITE.erl
index 72f6a0cee6..3f5f42c054 100644
--- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_1.erl
+++ b/lib/common_test/test/ct_surefire_SUITE_data/fail_SUITE.erl
@@ -1,8 +1,7 @@
-%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
@@ -14,19 +13,16 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-%%
+%%
%% %CopyrightEnd%
%%
--module(bin_syntax_1).
+-module(fail_SUITE).
+-include_lib("common_test/include/ct.hrl").
--export([f/2,?MODULE/0]).
+-export([all/0, test_case/1]).
-?MODULE() ->
- ok.
+all() ->
+ [test_case].
-f(X, Y) ->
- case X of
- a ->
- Y2 = 8
- end,
- <<5:Y2>> = Y.
+test_case(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_surefire_SUITE_data/fail_pre_init_per_suite.erl b/lib/common_test/test/ct_surefire_SUITE_data/fail_pre_init_per_suite.erl
new file mode 100644
index 0000000000..ff278db378
--- /dev/null
+++ b/lib/common_test/test/ct_surefire_SUITE_data/fail_pre_init_per_suite.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% This tests that the correct XML is produced when pre_init_per_suite
+%%% fails in a hook
+-module(fail_pre_init_per_suite).
+
+%% CT Hooks
+-export([init/2, pre_init_per_suite/3]).
+
+-type config() :: proplists:proplist().
+-type reason() :: term().
+-type skip_or_fail() :: skip | auto_skip | fail | 'EXIT'.
+
+-record(state, {}).
+
+-spec init(Id :: term(), Opts :: proplists:proplist()) ->
+ {ok, proplists:proplist()}.
+init(_Id, Opts) ->
+ {ok, Opts}.
+
+-spec pre_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | {skip_or_fail(), reason()}, NewState :: #state{}}.
+pre_init_per_suite(fail_SUITE, _Config, State) ->
+ {{fail, pre_init_per_suite}, State};
+pre_init_per_suite(_Suite, Config, State) ->
+ {Config, State}.
+
diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_5.erl b/lib/common_test/test/ct_surefire_SUITE_data/pass_SUITE.erl
index 7289d2b553..74ed5b730e 100644
--- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_5.erl
+++ b/lib/common_test/test/ct_surefire_SUITE_data/pass_SUITE.erl
@@ -1,8 +1,7 @@
-%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
@@ -14,16 +13,16 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-%%
+%%
%% %CopyrightEnd%
%%
--module(beam_compiler_5).
--export([beam_compiler_5/0]).
+-module(pass_SUITE).
+-include_lib("common_test/include/ct.hrl").
--compile(export_all).
+-export([all/0, test_case/1]).
-beam_compiler_5() ->
- ok.
+all() ->
+ [test_case].
-t() ->
- [_|_] = x.
+test_case(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 51ca691d69..477fcb8a26 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -43,6 +43,8 @@
-export([random_error/1]).
+-export([unique_timestamp/0]).
+
-include_lib("kernel/include/file.hrl").
%%%-----------------------------------------------------------------
@@ -110,7 +112,8 @@ start_slave(NodeName, Config, Level) ->
undefined -> [];
Ds -> Ds
end,
- PathDirs = [PrivDir,TSDir | AddPathDirs],
+ TestSupDir = filename:dirname(code:which(?MODULE)),
+ PathDirs = [PrivDir,TSDir,TestSupDir | AddPathDirs],
[true = rpc:call(CTNode, code, add_patha, [D]) || D <- PathDirs],
test_server:format(Level, "Dirs added to code path (on ~w):~n",
[CTNode]),
@@ -1430,7 +1433,21 @@ rm_files([F | Fs]) ->
end;
rm_files([]) ->
ok.
-
+
+unique_timestamp() ->
+ unique_timestamp(os:timestamp(), 100000).
+
+unique_timestamp(TS, 0) ->
+ TS;
+unique_timestamp(TS0, N) ->
+ case os:timestamp() of
+ TS0 ->
+ timer:sleep(1),
+ unique_timestamp(TS0, N-1);
+ TS1 ->
+ TS1
+ end.
+
%%%-----------------------------------------------------------------
%%%
slave_stop(Node) ->
diff --git a/lib/common_test/test_server/ts_run.erl b/lib/common_test/test_server/ts_run.erl
index a856fc6f8d..66db1ff9a7 100644
--- a/lib/common_test/test_server/ts_run.erl
+++ b/lib/common_test/test_server/ts_run.erl
@@ -261,7 +261,7 @@ run_batch(Vars, _Spec, State) ->
Command = State#state.command ++ " -noinput -s erlang halt",
ts_lib:progress(Vars, 1, "Command: ~ts~n", [Command]),
io:format(user, "Command: ~ts~n",[Command]),
- Port = open_port({spawn, Command}, [stream, in, eof]),
+ Port = open_port({spawn, Command}, [stream, in, eof, exit_status]),
Timeout = 30000 * case os:getenv("TS_RUN_VALGRIND") of
false -> 1;
_ -> 100
@@ -284,7 +284,16 @@ tricky_print_data(Port, Timeout) ->
ok
after 1 -> % force context switch
ok
- end
+ end,
+ receive
+ {Port, {exit_status, 0}} ->
+ ok;
+ {Port, {exit_status, N}} ->
+ io:format(user, "Test run exited with status ~p~n", [N])
+ after 1 ->
+ %% This shouldn't happen, but better safe then hanging
+ ok
+ end
after Timeout ->
case erl_epmd:names() of
{ok,Names} ->
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 9a81537006..f6ca7a0afb 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -230,9 +230,7 @@ build_attributes(Opts, SourceFile, Attr, MD5) ->
[_|_] -> [{source,SourceFile}]
end,
Misc = case member(slim, Opts) of
- false ->
- {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(),
- [{time,{Y,Mo,D,H,Mi,S}}|Misc0];
+ false -> Misc0;
true -> []
end,
Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc],
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index d033050d3c..6dc162db40 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -126,6 +126,7 @@
%% keep map exports here for now
c_map_pattern/1,
is_c_map/1,
+ is_c_map_pattern/1,
map_es/1,
map_arg/1,
update_c_map/3,
@@ -134,7 +135,7 @@
ann_c_map_pattern/2,
map_pair_op/1,map_pair_key/1,map_pair_val/1,
update_c_map_pair/4,
- c_map_pair/2,
+ c_map_pair/2, c_map_pair_exact/2,
ann_c_map_pair/4
]).
@@ -1636,6 +1637,11 @@ is_c_map_empty(#c_map{ es=[] }) -> true;
is_c_map_empty(#c_literal{val=M}) when is_map(M),map_size(M) =:= 0 -> true;
is_c_map_empty(_) -> false.
+-spec is_c_map_pattern(c_map()) -> boolean().
+
+is_c_map_pattern(#c_map{is_pat=IsPat}) ->
+ IsPat.
+
-spec ann_c_map([term()], [c_map_pair()]) -> c_map() | c_literal().
ann_c_map(As, Es) ->
@@ -1688,6 +1694,11 @@ map_pair_op(#c_map_pair{op=Op}) -> Op.
c_map_pair(Key,Val) ->
#c_map_pair{op=#c_literal{val=assoc},key=Key,val=Val}.
+-spec c_map_pair_exact(cerl(), cerl()) -> c_map_pair().
+
+c_map_pair_exact(Key,Val) ->
+ #c_map_pair{op=#c_literal{val=exact},key=Key,val=Val}.
+
-spec ann_c_map_pair([term()], cerl(), cerl(), cerl()) ->
c_map_pair().
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 6d38748964..b3decbec1f 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -61,6 +61,7 @@
map_arg/1, map_es/1,
ann_c_map/3,
update_c_map/3,
+ is_c_map_pattern/1, ann_c_map_pattern/2,
map_pair_key/1,map_pair_val/1,map_pair_op/1,
ann_c_map_pair/4,
update_c_map_pair/4
@@ -752,10 +753,17 @@ label(T, N, Env) ->
{As, N2} = label_ann(T, N1),
{ann_c_tuple_skel(As, Ts), N2};
map ->
- {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};
+ case is_c_map_pattern(T) of
+ false ->
+ {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};
+ true ->
+ {Ts, N1} = label_list(map_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_map_pattern(As, Ts), N2}
+ end;
map_pair ->
{Op, N1} = label(map_pair_op(T), N, Env),
{Key, N2} = label(map_pair_key(T), N1, Env),
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 65566df025..149086152a 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -1317,7 +1317,7 @@ generate_key(String) when is_list(String) ->
encrypt({des3_cbc=Type,Key,IVec,BlockSize}, Bin0) ->
Bin1 = case byte_size(Bin0) rem BlockSize of
0 -> Bin0;
- N -> list_to_binary([Bin0,crypto:rand_bytes(BlockSize-N)])
+ N -> list_to_binary([Bin0,crypto:strong_rand_bytes(BlockSize-N)])
end,
Bin = crypto:block_encrypt(Type, Key, IVec, Bin1),
TypeString = atom_to_list(Type),
@@ -1798,4 +1798,5 @@ pre_load() ->
v3_core,
v3_kernel,
v3_life],
- code:ensure_modules_loaded(L).
+ _ = code:ensure_modules_loaded(L),
+ ok.
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index 315324e906..8028aa99bb 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -47,12 +47,14 @@ receive_expr timeout try_expr
sequence catch_expr
variable clause clause_pattern
-map_expr map_pairs map_pair map_pair_assoc map_pair_exact
+map_expr anno_map_expr map_pairs anno_map_pair map_pair map_pair_assoc map_pair_exact
map_pattern map_pair_patterns map_pair_pattern
-annotation anno_fun anno_expression anno_expressions
+annotation anno_atom anno_fun anno_expression anno_expressions
anno_variable anno_variables anno_pattern anno_patterns
anno_function_name
+anno_literal
+anno_segment anno_segment_pattern
anno_clause anno_clauses.
Terminals
@@ -90,7 +92,7 @@ module_definition ->
module_definition ->
'(' 'module' atom module_export module_attribute module_defs 'end'
'-|' annotation ')' :
- #c_module{anno='$9',name=tok_val('$3'),exports='$4',
+ #c_module{anno='$9',name=#c_literal{val=tok_val('$3')},exports='$4',
attrs='$5',defs='$6'}.
module_export -> '[' ']' : [].
@@ -99,7 +101,7 @@ module_export -> '[' exported_names ']' : '$2'.
exported_names -> exported_name ',' exported_names : ['$1' | '$3'].
exported_names -> exported_name : ['$1'].
-exported_name -> function_name : '$1'.
+exported_name -> anno_function_name : '$1'.
module_attribute -> 'attributes' '[' ']' : [].
module_attribute -> 'attributes' '[' attribute_list ']' : '$3'.
@@ -107,8 +109,16 @@ module_attribute -> 'attributes' '[' attribute_list ']' : '$3'.
attribute_list -> attribute ',' attribute_list : ['$1' | '$3'].
attribute_list -> attribute : ['$1'].
-attribute -> atom '=' literal :
- {#c_literal{val=tok_val('$1')},'$3'}.
+attribute -> anno_atom '=' anno_literal :
+ {'$1','$3'}.
+
+anno_atom -> atom :
+ cerl:c_atom(tok_val('$1')).
+anno_atom -> '(' atom '-|' annotation ')' :
+ cerl:ann_c_atom('$4', tok_val('$2')).
+
+anno_literal -> literal : '$1'.
+anno_literal -> '(' literal '-|' annotation ')' : cerl:set_ann('$2', '$4').
module_defs -> function_definitions : '$1'.
@@ -186,7 +196,9 @@ tuple_pattern -> '{' anno_patterns '}' : c_tuple('$2').
map_pattern -> '~' '{' '}' '~' : c_map_pattern([]).
map_pattern -> '~' '{' map_pair_patterns '}' '~' :
- c_map_pattern(lists:sort('$3')).
+ c_map_pattern('$3').
+map_pattern -> '~' '{' map_pair_patterns '|' anno_map_expr '}' '~' :
+ ann_c_map_pattern('$5', '$3').
map_pair_patterns -> map_pair_pattern : ['$1'].
map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
@@ -194,6 +206,9 @@ map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
map_pair_pattern -> anno_expression ':=' anno_pattern :
#c_map_pair{op=#c_literal{val=exact},
key='$1',val='$3'}.
+map_pair_pattern -> '(' anno_expression ':=' anno_pattern '-|' annotation ')' :
+ #c_map_pair{anno='$6',op=#c_literal{val=exact},
+ key='$2',val='$4'}.
cons_pattern -> '[' anno_pattern tail_pattern :
c_cons('$2', '$3').
@@ -206,8 +221,12 @@ tail_pattern -> ',' anno_pattern tail_pattern :
binary_pattern -> '#' '{' '}' '#' : #c_binary{segments=[]}.
binary_pattern -> '#' '{' segment_patterns '}' '#' : #c_binary{segments='$3'}.
-segment_patterns -> segment_pattern ',' segment_patterns : ['$1' | '$3'].
-segment_patterns -> segment_pattern : ['$1'].
+segment_patterns -> anno_segment_pattern ',' segment_patterns : ['$1' | '$3'].
+segment_patterns -> anno_segment_pattern : ['$1'].
+
+anno_segment_pattern -> segment_pattern : '$1'.
+anno_segment_pattern -> '(' segment_pattern '-|' annotation ')' :
+ cerl:set_ann('$2', '$4').
segment_pattern -> '#' '<' anno_pattern '>' '(' anno_expressions ')':
case '$6' of
@@ -289,11 +308,17 @@ tuple -> '{' anno_expressions '}' : c_tuple('$2').
map_expr -> '~' '{' '}' '~' : c_map([]).
map_expr -> '~' '{' map_pairs '}' '~' : c_map('$3').
-map_expr -> '~' '{' map_pairs '|' variable '}' '~' : ann_c_map([], '$5', '$3').
-map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : ann_c_map([], '$5', '$3').
+map_expr -> '~' '{' map_pairs '|' anno_variable '}' '~' : ann_c_map([], '$5', '$3').
+map_expr -> '~' '{' map_pairs '|' anno_map_expr '}' '~' : ann_c_map([], '$5', '$3').
-map_pairs -> map_pair : ['$1'].
-map_pairs -> map_pair ',' map_pairs : ['$1' | '$3'].
+anno_map_expr -> map_expr : '$1'.
+anno_map_expr -> '(' map_expr '-|' annotation ')' : cerl:set_ann('$2', '$4').
+
+map_pairs -> anno_map_pair : ['$1'].
+map_pairs -> anno_map_pair ',' map_pairs : ['$1' | '$3'].
+
+anno_map_pair -> map_pair : '$1'.
+anno_map_pair -> '(' map_pair '-|' annotation ')' : cerl:set_ann('$2', '$4').
map_pair -> map_pair_assoc : '$1'.
map_pair -> map_pair_exact : '$1'.
@@ -312,8 +337,11 @@ tail -> ',' anno_expression tail : c_cons('$2', '$3').
binary -> '#' '{' '}' '#' : #c_literal{val = <<>>}.
binary -> '#' '{' segments '}' '#' : make_binary('$3').
-segments -> segment ',' segments : ['$1' | '$3'].
-segments -> segment : ['$1'].
+segments -> anno_segment ',' segments : ['$1' | '$3'].
+segments -> anno_segment : ['$1'].
+
+anno_segment -> segment : '$1'.
+anno_segment -> '(' segment '-|' annotation ')' : cerl:set_ann('$2', '$4').
segment -> '#' '<' anno_expression '>' '(' anno_expressions ')':
case '$6' of
@@ -413,7 +441,8 @@ Erlang code.
-include("core_parse.hrl").
--import(cerl, [ann_c_map/3,c_cons/2,c_map/1,c_map_pattern/1,c_tuple/1]).
+-import(cerl, [ann_c_map/3,ann_c_map_pattern/2,c_cons/2,c_map/1,
+ c_map_pattern/1,c_tuple/1]).
tok_val(T) -> element(3, T).
tok_line(T) -> element(2, T).
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index 78a081e9ca..f34a5c034f 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -21,7 +21,7 @@
-module(core_pp).
--export([format/1]).
+-export([format/1,format_all/1]).
-include("core_parse.hrl").
@@ -37,21 +37,32 @@
indent = 0 :: integer(),
item_indent = 2 :: integer(),
body_indent = 4 :: integer(),
- tab_width = 8 :: non_neg_integer(),
- line = 0 :: integer()}).
+ line = 0 :: integer(),
+ clean = true :: boolean()}).
+
+-define(TAB_WIDTH, 8).
-spec format(cerl:cerl()) -> iolist().
format(Node) ->
format(Node, #ctxt{}).
-maybe_anno(Node, Fun, Ctxt) ->
+-spec format_all(cerl:cerl()) -> iolist().
+
+format_all(Node) ->
+ format(Node, #ctxt{clean=false}).
+
+maybe_anno(Node, Fun, #ctxt{clean=false}=Ctxt) ->
As = cerl:get_ann(Node),
- case get_line(As) of
+ maybe_anno(Node, Fun, Ctxt, As);
+maybe_anno(Node, Fun, #ctxt{clean=true}=Ctxt) ->
+ As0 = cerl:get_ann(Node),
+ case get_line(As0) of
none ->
- maybe_anno(Node, Fun, Ctxt, As);
+ maybe_anno(Node, Fun, Ctxt, As0);
Line ->
- if Line > Ctxt#ctxt.line ->
+ As = strip_line(As0),
+ if Line > Ctxt#ctxt.line ->
[io_lib:format("%% Line ~w",[Line]),
nl_indent(Ctxt),
maybe_anno(Node, Fun, Ctxt#ctxt{line = Line}, As)
@@ -61,22 +72,22 @@ maybe_anno(Node, Fun, Ctxt) ->
end
end.
-maybe_anno(Node, Fun, Ctxt, As) ->
- case strip_line(As) of
- [] ->
- Fun(Node, Ctxt);
- List ->
- Ctxt1 = add_indent(Ctxt, 2),
- Ctxt2 = add_indent(Ctxt1, 3),
- ["( ",
- Fun(Node, Ctxt1),
- nl_indent(Ctxt1),
- "-| ",format_anno(List, Ctxt2)," )"
- ]
- end.
+maybe_anno(Node, Fun, Ctxt, []) ->
+ Fun(Node, Ctxt);
+maybe_anno(Node, Fun, Ctxt, List) ->
+ Ctxt1 = add_indent(Ctxt, 2),
+ Ctxt2 = add_indent(Ctxt1, 3),
+ ["( ",
+ Fun(Node, Ctxt1),
+ nl_indent(Ctxt1),
+ "-| ",format_anno(List, Ctxt2)," )"
+ ].
format_anno([_|_]=List, Ctxt) ->
[$[,format_anno_list(List, Ctxt),$]];
+format_anno({file,Name}, _Ctxt) ->
+ %% Optimization: Reduces file size considerably.
+ io_lib:format("{'file',~p}", [Name]);
format_anno(Tuple, Ctxt) when is_tuple(Tuple) ->
[${,format_anno_list(tuple_to_list(Tuple), Ctxt),$}];
format_anno(Val, Ctxt) when is_atom(Val) ->
@@ -172,7 +183,8 @@ format_1(#c_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
$}
];
-format_1(#c_map{arg=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 ->
+format_1(#c_map{arg=#c_literal{anno=[],val=M},es=Es}, Ctxt)
+ when is_map(M), map_size(M) =:= 0 ->
["~{",
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
"}~"
@@ -195,9 +207,16 @@ format_1(#c_values{es=Es}, Ctxt) ->
format_1(#c_alias{var=V,pat=P}, Ctxt) ->
Txt = [format(V, Ctxt)|" = "],
[Txt|format(P, add_indent(Ctxt, width(Txt, Ctxt)))];
-format_1(#c_let{vars=Vs0,arg=A,body=B}, Ctxt) ->
- Vs = [cerl:set_ann(V, []) || V <- Vs0],
- case is_simple_term(A) of
+format_1(#c_let{anno=Anno0,vars=Vs0,arg=A0,body=B}, #ctxt{clean=Clean}=Ctxt) ->
+ {Vs,A,Anno} = case Clean of
+ false ->
+ {Vs0,A0,Anno0};
+ true ->
+ {[cerl:set_ann(V, []) || V <- Vs0],
+ cerl:set_ann(A0, []),
+ []}
+ end,
+ case is_simple_term(A) andalso Anno =:= [] of
false ->
Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
["let ",
@@ -214,7 +233,7 @@ format_1(#c_let{vars=Vs0,arg=A,body=B}, Ctxt) ->
["let ",
format_values(Vs, add_indent(Ctxt, 4)),
" = ",
- format(cerl:set_ann(A, []), Ctxt1),
+ format(A, Ctxt1),
nl_indent(Ctxt),
"in "
| format(B, add_indent(Ctxt, 4))
@@ -362,7 +381,10 @@ format_values(Vs, Ctxt) ->
format_hseq(Vs, ",", add_indent(Ctxt, 1), fun format/2),
$>].
-format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
+format_bitstr(Node, Ctxt) ->
+ maybe_anno(Node, fun do_format_bitstr/2, Ctxt).
+
+do_format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
Vs = [S, U, T, Fs],
Ctxt1 = add_indent(Ctxt0, 2),
Val = format(V, Ctxt1),
@@ -387,7 +409,7 @@ format_clause_1(#c_clause{pats=Ps,guard=G,body=B}, Ctxt) ->
width(Ptxt, Ctxt) + 6))];
false ->
[nl_indent(Ctxt2), "when ",
- format_guard(G, add_indent(Ctxt2, 2))]
+ format_guard(G, add_indent(set_class(Ctxt2, expr), 2))]
end++
" ->",
nl_indent(Ctxt2)
@@ -449,37 +471,46 @@ format_map_pair(Op, K, V, Ctxt0) ->
Ctxt2 = add_indent(Ctxt0, width(Txt, Ctxt1)),
[Txt,Op,format(V, Ctxt2)].
-indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
-
-indent(N, _) when N =< 0 -> "";
-indent(N, Ctxt) ->
- T = Ctxt#ctxt.tab_width,
- string:chars($\t, N div T, string:chars($\s, N rem T)).
+indent(#ctxt{indent=N}) ->
+ if
+ N =< 0 ->
+ "";
+ true ->
+ string:chars($\t, N div ?TAB_WIDTH, spaces(N rem ?TAB_WIDTH))
+ end.
nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
+spaces(0) -> "";
+spaces(1) -> " ";
+spaces(2) -> " ";
+spaces(3) -> " ";
+spaces(4) -> " ";
+spaces(5) -> " ";
+spaces(6) -> " ";
+spaces(7) -> " ".
unindent(T, Ctxt) ->
- unindent(T, Ctxt#ctxt.indent, Ctxt, []).
+ unindent(T, Ctxt#ctxt.indent, []).
-unindent(T, N, _, C) when N =< 0 ->
+unindent(T, N, C) when N =< 0 ->
[T|C];
-unindent([$\s|T], N, Ctxt, C) ->
- unindent(T, N - 1, Ctxt, C);
-unindent([$\t|T], N, Ctxt, C) ->
- Tab = Ctxt#ctxt.tab_width,
+unindent([$\s|T], N, C) ->
+ unindent(T, N - 1, C);
+unindent([$\t|T], N, C) ->
+ Tab = ?TAB_WIDTH,
if N >= Tab ->
- unindent(T, N - Tab, Ctxt, C);
+ unindent(T, N - Tab, C);
true ->
- unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
+ unindent([spaces(Tab - N)|T], 0, C)
end;
-unindent([L|T], N, Ctxt, C) when is_list(L) ->
- unindent(L, N, Ctxt, [T|C]);
-unindent([H|T], _, _, C) ->
+unindent([L|T], N, C) when is_list(L) ->
+ unindent(L, N, [T|C]);
+unindent([H|T], _, C) ->
[H|[T|C]];
-unindent([], N, Ctxt, [H|T]) ->
- unindent(H, N, Ctxt, T);
-unindent([], _, _, []) -> [].
+unindent([], N, [H|T]) ->
+ unindent(H, N, T);
+unindent([], _, []) -> [].
width(Txt, Ctxt) ->
@@ -488,7 +519,7 @@ width(Txt, Ctxt) ->
end.
width([$\t|T], A, Ctxt, C) ->
- width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
+ width(T, A + ?TAB_WIDTH, Ctxt, C);
width([$\n|T], _, Ctxt, C) ->
width(unindent([T|C], Ctxt), Ctxt);
width([H|T], A, Ctxt, C) when is_list(H) ->
diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl
index 2b89dc628c..936c5f6106 100644
--- a/lib/compiler/src/rec_env.erl
+++ b/lib/compiler/src/rec_env.erl
@@ -603,7 +603,8 @@ generate(_N, Range) ->
%% same BEAM code.
case rand:export_seed() of
undefined ->
- rand:seed(exsplus, {1,42,2053});
+ _ = rand:seed(exsplus, {1,42,2053}),
+ ok;
_ ->
ok
end,
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index de775097e3..83b3650180 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -510,8 +510,16 @@ unforce(_, Vs) -> Vs.
exprs([E0|Es0], St0) ->
{E1,Eps,St1} = expr(E0, St0),
- {Es1,St2} = exprs(Es0, St1),
- {Eps ++ [E1] ++ Es1,St2};
+ case E1 of
+ #iprimop{name=#c_literal{val=match_fail}} ->
+ %% Must discard the rest of the body, because it
+ %% may refer to variables that have not been bound.
+ %% Example: {ok={error,E}} = foo(), E.
+ {Eps ++ [E1],St1};
+ _ ->
+ {Es1,St2} = exprs(Es0, St1),
+ {Eps ++ [E1] ++ Es1,St2}
+ end;
exprs([], St) -> {[],St}.
%% expr(Expr, State) -> {Cexpr,[PreExp],State}.
@@ -681,9 +689,14 @@ expr({match,L,P0,E0}, St0) ->
Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])),
case P2 of
nomatch ->
- St = add_warning(L, nomatch, St5),
- {#icase{anno=#a{anno=Lanno},
- args=[E2],clauses=[],fc=Fc},Eps1++Eps2,St};
+ St6 = add_warning(L, nomatch, St5),
+ {Expr,Eps3,St} = safe(E1, St6),
+ Eps = Eps1 ++ Eps2 ++ Eps3,
+ Badmatch = c_tuple([#c_literal{val=badmatch},Expr]),
+ Fail = #iprimop{anno=#a{anno=Lanno},
+ name=#c_literal{val=match_fail},
+ args=[Badmatch]},
+ {Fail,Eps,St};
Other when not is_atom(Other) ->
{#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps1++Eps2,St5}
end;
@@ -784,7 +797,7 @@ badmap_term(_Map, #core{in_guard=true}) ->
%% since it is not user-visible.
#c_literal{val=badmap};
badmap_term(Map, #core{in_guard=false}) ->
- #c_tuple{es=[#c_literal{val=badmap},Map]}.
+ c_tuple([#c_literal{val=badmap},Map]).
map_build_pairs(Map, Es0, Ann, St0) ->
{Es,Pre,St1} = map_build_pairs_1(Es0, St0),
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 4a6330fce4..402e3c4912 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -147,6 +147,7 @@ include_attribute(callback) -> false;
include_attribute(opaque) -> false;
include_attribute(export_type) -> false;
include_attribute(record) -> false;
+include_attribute(optional_callbacks) -> false;
include_attribute(_) -> true.
function({#c_var{name={F,Arity}=FA},Body}, St0) ->
diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl
index a9bbe31b59..81f8d10687 100644
--- a/lib/compiler/test/beam_block_SUITE.erl
+++ b/lib/compiler/test/beam_block_SUITE.erl
@@ -21,7 +21,11 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- get_map_elements/1]).
+ get_map_elements/1,otp_7345/1]).
+
+%% The only test for the following functions is that
+%% the code compiles and is accepted by beam_validator.
+-export([encode_wildcards3/4,find_operands/4]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -64,3 +68,114 @@ get_map_elements([{Pred,Var}|Left], Map, Acc) ->
end;
get_map_elements([], _Map, Acc) ->
Acc.
+
+%% The following code
+%%
+%% {get_list,{x,2},{x,0},{x,1}}.
+%% {gc_bif,length,{f,0},1,[{x,0}],{x,0}}.
+%% {move,{x,0},{x,1}}.
+%%
+%% was incorrectly optimized to
+%%
+%% {get_list,{x,2},{x,0},{y,0}}.
+%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}}.
+%%
+%% because beam_block:is_transparent({x,1},
+%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}}
+%% incorrectly returned true.
+
+-record(contextId,{cid,device_type,contextRef}).
+-record(dpRef,{cid,tlli,ms_device_context_id}).
+-record(qosProfileBssgp,{peak_bit_rate_msb,
+ peak_bit_rate_lsb,
+ t_a_precedence}).
+-record(llUnitdataReq,{sapi,
+ l3_pdu_length,
+ pdu_life}).
+-record(ptmsi,{value}).
+
+otp_7345(_Config) ->
+ #llUnitdataReq{l3_pdu_length=3,pdu_life=4} =
+ otp_7345(#contextId{}, 0, [[1,2,3],4,5]).
+
+
+otp_7345(ObjRef, _RdEnv, Args) ->
+ Cid = ObjRef#contextId.cid,
+ _ = #dpRef{cid = Cid,
+ ms_device_context_id = cid_id,
+ tlli = #ptmsi{value = 0}},
+ _ = #qosProfileBssgp{peak_bit_rate_msb = 0,
+ peak_bit_rate_lsb = 80,
+ t_a_precedence = 49},
+ [Cpdu|_] = Args,
+ LlUnitdataReq =
+ #llUnitdataReq{sapi = 7,
+ l3_pdu_length = length(Cpdu),
+ pdu_life =
+ id(42)
+ div
+ 10},
+ id(LlUnitdataReq).
+
+%%%
+%%% The only test of the following code is that it compiles.
+%%%
+
+%% Slightly simplifed from megaco_binary_term_id_gen.
+%% beam_block failed to note that the {gc_bif,'-'...} instruction could
+%% fail, and that therefore {y,0} need to be initialized.
+%% {allocate,8,6}.
+%% %% {init,{y,0}} needed here.
+%% {get_list,{x,1},{x,6},{x,7}}.
+%% {'catch',{y,7},{f,3}}.
+%% {move,{x,4},{y,1}}.
+%% {move,{x,3},{y,2}}.
+%% {move,{x,2},{y,3}}.
+%% {move,{x,5},{y,4}}.
+%% {move,{x,7},{y,5}}.
+%% {move,{x,6},{y,6}}.
+%% {gc_bif,'-',{f,0},8,[{x,3},{x,6}],{x,0}}.
+%% {move,{x,0},{y,0}}.
+
+encode_wildcards3([],[],_,_) -> [];
+encode_wildcards3([Level|Levels],[BitsInLevel|BitsRest],LevelNo,TotSize) ->
+ case (catch ?MODULE:encode_wildcard(Level,BitsInLevel,TotSize-BitsInLevel,
+ length(Levels))) of
+ {'EXIT',{Reason,Info}} ->
+ exit({Reason,{LevelNo,Info}});
+
+ no_wildcard ->
+ encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel);
+
+ {level,Wl} ->
+ [Wl|
+ encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel)];
+
+ {recursive,Wr} ->
+ [Wr]
+ end.
+
+%% Slightly simplified code from hipe_rtl_ssapre.
+%% beam_block used to do the following incorrect optimization:
+%%
+%% {gc_bif,length,{f,0},1,[{x,0}],{x,3}}.
+%% ^^^^^ Was {x,0} - changing to {x,3} is not safe.
+%% {gc_bif,'+',{f,0},0,[{y,2},{integer,1}],{x,0}}.
+%% ^^^ Only one register live
+%% . . .
+%% {call_last,4,{f,2},4}. %% beam_validator noted that {x,3} wasn't live.
+
+find_operands(Cfg,XsiGraph,[],_Count) ->
+ {Cfg,XsiGraph};
+find_operands(Cfg,XsiGraph,ActiveList,Count) ->
+ {NewCfg,TempActiveList}=?MODULE:find_operands_for_active_list(Cfg,XsiGraph,
+ ActiveList,[]),
+ NewActiveList=lists:reverse(TempActiveList),
+ [Count+1, length(NewActiveList), length(digraph:vertices(XsiGraph))],
+ find_operands(NewCfg,XsiGraph,NewActiveList,Count+1).
+
+%%%
+%%% Common functions.
+%%%
+
+id(I) -> I.
diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl
index 70c00f163c..6353ed3242 100644
--- a/lib/compiler/test/beam_utils_SUITE.erl
+++ b/lib/compiler/test/beam_utils_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
apply_fun/1,apply_mf/1,bs_init/1,bs_save/1,
is_not_killed/1,is_not_used_at/1,
- select/1,y_catch/1]).
+ select/1,y_catch/1,otp_8949_b/1,liveopt/1]).
-export([id/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -41,7 +41,9 @@ groups() ->
is_not_killed,
is_not_used_at,
select,
- y_catch
+ y_catch,
+ otp_8949_b,
+ liveopt
]}].
init_per_suite(Config) ->
@@ -232,6 +234,40 @@ do_y_catch_1(<<_,_/binary>>, _) ->
do_y_catch_2(_) -> {a,b,c}.
+otp_8949_b(_Config) ->
+ self() ! something,
+ value = otp_8949_b([], false),
+ {'EXIT',_} = (catch otp_8949_b([], true)),
+ ok.
+
+%% Would cause an endless loop in beam_utils.
+otp_8949_b(A, B) ->
+ Var = id(value),
+ if
+ A == [], B == false ->
+ ok
+ end,
+ receive
+ something ->
+ id(Var)
+ end.
+
+-record(alarmInfo, {type,cause,origin}).
+
+liveopt(_Config) ->
+ F = liveopt_fun(42, pebkac, user),
+ void = F(42, #alarmInfo{type=sctp,cause=pebkac,origin=user}),
+ ok.
+
+liveopt_fun(Peer, Cause, Origin) ->
+ fun(PeerNo, AlarmInfo)
+ when PeerNo == Peer andalso
+ AlarmInfo == #alarmInfo{type=sctp,
+ cause=Cause,
+ origin=Origin} ->
+ void
+ end.
+
%% The identity function.
id(I) -> I.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index c2d146a0b1..224abf6c29 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -24,11 +24,11 @@
-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,
- fun_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1,
+ size_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1,
bin_tail/1,save_restore/1,
partitioned_bs_match/1,function_clause/1,
unit/1,shared_sub_bins/1,bin_and_float/1,
- dec_subidentifiers/1,skip_optional_tag/1,
+ dec_subidentifiers/1,skip_optional_tag/1,decode_integer/1,
wfbm/1,degenerated_match/1,bs_sum/1,coverage/1,
multiple_uses/1,zero_label/1,followed_by_catch/1,
matching_meets_construction/1,simon/1,matching_and_andalso/1,
@@ -38,7 +38,7 @@
no_partition/1,calling_a_binary/1,binary_in_map/1,
match_string_opt/1,select_on_integer/1,
map_and_binary/1,unsafe_branch_caching/1,
- bad_literals/1,good_literals/1]).
+ bad_literals/1,good_literals/1,constant_propagation/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -56,11 +56,11 @@ all() ->
groups() ->
[{p,[parallel],
- [fun_shadow,int_float,otp_5269,null_fields,wiger,
+ [size_shadow,int_float,otp_5269,null_fields,wiger,
bin_tail,save_restore,
partitioned_bs_match,function_clause,unit,
shared_sub_bins,bin_and_float,dec_subidentifiers,
- skip_optional_tag,wfbm,degenerated_match,bs_sum,
+ skip_optional_tag,decode_integer,wfbm,degenerated_match,bs_sum,
coverage,multiple_uses,zero_label,followed_by_catch,
matching_meets_construction,simon,
matching_and_andalso,otp_7188,otp_7233,otp_7240,
@@ -69,7 +69,7 @@ groups() ->
no_partition,calling_a_binary,binary_in_map,
match_string_opt,select_on_integer,
map_and_binary,unsafe_branch_caching,
- bad_literals,good_literals]}].
+ bad_literals,good_literals,constant_propagation]}].
init_per_suite(Config) ->
@@ -91,33 +91,54 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
ok.
-fun_shadow(Config) when is_list(Config) ->
- %% OTP-5270
- 7 = fun_shadow_1(),
- 7 = fun_shadow_2(8),
- 7 = fun_shadow_3(),
- no = fun_shadow_4(8),
+size_shadow(Config) when is_list(Config) ->
+ %% Originally OTP-5270.
+ 7 = size_shadow_1(),
+ 7 = size_shadow_2(8),
+ 7 = size_shadow_3(),
+ no = size_shadow_4(8),
+ Any = {any,term,goes},
+ {2577,Any,-175,whatever} =
+ (size_shadow_5(Any, 12))(<<2577:12>>, -175, whatever),
+ {7777,Any,42,whatever} =
+ (size_shadow_6(Any, 13))(42, <<7777:13>>, whatever),
+ {<<45>>,<<>>} = size_shadow_7({int,1}, <<1:16,45>>),
+ {'EXIT',{function_clause,_}} =
+ (catch size_shadow_7({int,42}, <<1:16,45>>)),
ok.
-fun_shadow_1() ->
+size_shadow_1() ->
L = 8,
F = fun(<<L:L,B:L>>) -> B end,
F(<<16:8, 7:16>>).
-fun_shadow_2(L) ->
+size_shadow_2(L) ->
F = fun(<<L:L,B:L>>) -> B end,
F(<<16:8, 7:16>>).
-fun_shadow_3() ->
+size_shadow_3() ->
L = 8,
F = fun(<<L:L,B:L,L:L>>) -> B end,
F(<<16:8, 7:16,16:16>>).
-fun_shadow_4(L) ->
+size_shadow_4(L) ->
F = fun(<<L:L,B:L,L:L>>) -> B;
(_) -> no end,
F(<<16:8, 7:16,15:16>>).
+size_shadow_5(X, Y) ->
+ fun (<< A:Y >>, Y, B) -> fum(A, X, Y, B) end.
+
+size_shadow_6(X, Y) ->
+ fun (Y, << A:Y >>, B) -> fum(A, X, Y, B) end.
+
+fum(A, B, C, D) ->
+ {A,B,C,D}.
+
+size_shadow_7({int,N}, <<N:16,B:N/binary,T/binary>>) ->
+ {B,T}.
+
+
int_float(Config) when is_list(Config) ->
%% OTP-5323
<<103133.0:64/float>> = <<103133:64/float>>,
@@ -504,6 +525,23 @@ skip_optional_tag(<<Tag,RestTag/binary>>, <<Tag,Rest/binary>>) ->
skip_optional_tag(RestTag, Rest);
skip_optional_tag(_, _) -> missing.
+decode_integer(_Config) ->
+ {10795,<<43>>,whatever} = decode_integer(1, <<42,43>>, whatever),
+ {-28909,<<19>>,whatever} = decode_integer(1, <<143,19>>, whatever),
+ ok.
+
+decode_integer(Len, <<B1:1,B2:7,Bs/binary>>, RemovedBytes) when B1 == 0 ->
+ Bin = <<_Skip:Len/unit:8, Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>,
+ Size = byte_size(Bin),
+ <<Int:Size/unit:8>> = Bin,
+ {Int,Buffer2,RemovedBytes};
+decode_integer(Len, <<B1:1,B2:7,Bs/binary>>, RemovedBytes) ->
+ Bin = <<_Skip:Len/unit:8,Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>,
+ Size = byte_size(Bin),
+ <<N:Size/unit:8>> = <<B2,Bs/binary>>,
+ Int = N - (1 bsl (8 * size(Bin) -1)),
+ {Int,Buffer2,RemovedBytes}.
+
-define(DATELEN, 16).
wfbm(Config) when is_list(Config) ->
@@ -1387,6 +1425,32 @@ good_literals(_Config) ->
<<16#cafebeef:4/unit:8>> = id(<<16#cafebeef:32>>),
ok.
+constant_propagation(_Config) ->
+ <<5>> = constant_propagation_a(a, <<5>>),
+ {'EXIT',{{case_clause,b},_}} = (catch constant_propagation_a(b, <<5>>)),
+ 258 = constant_propagation_b(<<1,2>>),
+ F = constant_propagation_c(),
+ 259 = F(<<1,3>>),
+ ok.
+
+constant_propagation_a(X, Y) ->
+ case X of
+ a -> Y2 = 8
+ end,
+ <<5:Y2>> = Y.
+
+constant_propagation_b(B) ->
+ Sz = 16,
+ <<X:Sz/integer>> = B,
+ X.
+
+constant_propagation_c() ->
+ Size = 16,
+ fun(Bin) ->
+ <<X:Size/integer>> = Bin,
+ X
+ end.
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl
index 6961e625fd..cd1bc099e9 100644
--- a/lib/compiler/test/compilation_SUITE.erl
+++ b/lib/compiler/test/compilation_SUITE.erl
@@ -19,11 +19,48 @@
%%% Purpose : Compiles various modules with tough code
-module(compilation_SUITE).
+-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ beam_compiler_4/1,
+ beam_compiler_6/1,
+ beam_compiler_7/1,
+ beam_compiler_8/1,
+ beam_compiler_9/1,
+ beam_compiler_10/1,
+ beam_compiler_11/1,
+ compiler_1/1,
+ const_list_256/1,
+ convopts/1,
+ live_var/1,
+ on_load/1,
+ on_load_inline/1,
+ opt_crash/1,
+ otp_2330/1,
+ otp_2380/1,
+ otp_4790/1,
+ otp_5151/1,
+ otp_5235/1,
+ otp_5404/1,
+ otp_5436/1,
+ otp_5481/1,
+ otp_5553/1,
+ otp_5632/1,
+ otp_5714/1,
+ otp_5872/1,
+ otp_6121/1,
+ otp_7202/1,
+ otp_8949_a/1,
+ redundant_case/1,
+ self_compile/1,
+ self_compile_old_inliner/1,
+ split_cases/1,
+ string_table/1,
+ vsn_1/1,
+ vsn_2/1,
+ vsn_3/1]).
-include_lib("common_test/include/ct.hrl").
--compile(export_all).
-
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,10}}].
@@ -37,23 +74,18 @@ groups() ->
[{vsn,[parallel],[vsn_1,vsn_2,vsn_3]},
{p,test_lib:parallel(),
[compiler_1,
- compiler_3,compiler_5,beam_compiler_1,
- beam_compiler_2,beam_compiler_3,beam_compiler_4,
- beam_compiler_5,beam_compiler_6,beam_compiler_7,
+ beam_compiler_4,beam_compiler_6,beam_compiler_7,
beam_compiler_8,beam_compiler_9,beam_compiler_10,
- beam_compiler_11,beam_compiler_12,
- nested_tuples_in_case_expr,otp_2330,guards,
- {group,vsn},otp_2380,otp_2141,otp_2173,otp_4790,
- const_list_256,bin_syntax_1,bin_syntax_2,
- bin_syntax_3,bin_syntax_4,bin_syntax_5,bin_syntax_6,
- live_var,convopts,
- catch_in_catch,redundant_case,long_string,otp_5076,
- complex_guard,otp_5092,otp_5151,otp_5235,otp_5244,
- trycatch_4,opt_crash,otp_5404,otp_5436,otp_5481,
+ beam_compiler_11,
+ otp_2330,
+ {group,vsn},otp_2380,otp_4790,
+ const_list_256,live_var,convopts,
+ redundant_case,
+ otp_5151,otp_5235,
+ opt_crash,otp_5404,otp_5436,otp_5481,
otp_5553,otp_5632,otp_5714,otp_5872,otp_6121,
- otp_6121a,otp_6121b,otp_7202,otp_7345,on_load,
- string_table,otp_8949_a,otp_8949_b,split_cases,
- beam_utils_liveopt]}].
+ otp_7202,on_load,on_load_inline,
+ string_table,otp_8949_a,split_cases]}].
init_per_suite(Config) ->
Config.
@@ -70,85 +102,25 @@ end_per_group(_GroupName, Config) ->
-define(comp(N),
N(Config) when is_list(Config) -> try_it(N, Config)).
--define(comp_fail(N),
- N(Config) when is_list(Config) -> failure(N, Config)).
-
?comp(compiler_1).
-?comp(compiler_3).
-?comp(compiler_4).
-?comp(compiler_5).
-?comp(beam_compiler_1).
-?comp(beam_compiler_2).
-?comp(beam_compiler_3).
?comp(beam_compiler_4).
-?comp(beam_compiler_5).
?comp(beam_compiler_6).
?comp(beam_compiler_8).
?comp(beam_compiler_9).
?comp(beam_compiler_10).
?comp(beam_compiler_11).
-?comp(beam_compiler_12).
-?comp(beam_compiler_13).
-
-?comp(nested_tuples_in_case_expr).
?comp(otp_2330).
?comp(otp_2380).
-?comp(otp_2141).
-?comp(otp_2173).
?comp(otp_4790).
?comp(otp_5235).
-?comp(otp_5244).
-
-?comp(guards).
-
-?comp(pattern_expr).
-
?comp(const_list_256).
-?comp(bin_syntax_1).
-?comp(bin_syntax_2).
-?comp(bin_syntax_3).
-?comp(bin_syntax_4).
-
-?comp(bin_syntax_6).
-
-?comp(otp_5076).
-
-?comp(complex_guard).
-
-?comp(otp_5092).
?comp(otp_5151).
-%%% By Per Gustafsson <[email protected]>
-
-bin_syntax_5(Config) when is_list(Config) ->
- {<<45>>,<<>>} = split({int, 1}, <<1:16,45>>).
-
-split({int, N}, <<N:16,B:N/binary,T/binary>>) ->
- {B,T}.
-
-%% This program works with the old version of the compiler
-%% but, the core erlang that it produces have the same variable appearing
-%% looks like this:
-%%
-%% split({int, N}, <<_core1:16, B:N/binary, T/binary>>) when _core1==N
-%%
-%% with my change it will look like this:
-%%
-%% split({int, N}, <<_core1:16, B:_core1/binary, T/binary>>) when _core1==N
-%%
-%% This means that everything worked fine as long as the pattern
-%% matching order was left-to-right but on core erlang any order should be possible
-
?comp(live_var).
-
-?comp(trycatch_4).
-
-?comp(catch_in_catch).
-
?comp(opt_crash).
?comp(otp_5404).
@@ -159,8 +131,6 @@ split({int, N}, <<N:16,B:N/binary,T/binary>>) ->
?comp(otp_5714).
?comp(otp_5872).
?comp(otp_6121).
-?comp(otp_6121a).
-?comp(otp_6121b).
?comp(convopts).
?comp(otp_7202).
?comp(on_load).
@@ -193,91 +163,38 @@ redundant_case_1(3) -> d;
redundant_case_1(4) -> d;
redundant_case_1(_) -> d.
-failure(Module, Conf) ->
- Src = filename:join(proplists:get_value(data_dir, Conf),
- atom_to_list(Module)),
- Out = proplists:get_value(priv_dir, Conf),
- io:format("Compiling: ~ts\n", [Src]),
- CompRc = compile:file(Src, [{outdir,Out},return,time]),
- io:format("Result: ~p\n",[CompRc]),
- case CompRc of
- error -> ok;
- {error,Errors,_} -> check_errors(Errors);
- _ -> ct:fail({no_error, CompRc})
- end,
- ok.
-
-check_errors([{_,Eds}|T]) ->
- check_error(Eds),
- check_errors(T);
-check_errors([]) -> ok.
-
-check_error([{_,Mod,Error}|T]) ->
- check_error_1(Mod:format_error(Error)),
- check_error(T);
-check_error([{Mod,Error}|T]) ->
- check_error_1(Mod:format_error(Error)),
- check_error(T);
-check_error([]) -> ok.
-
-check_error_1(Str0) ->
- Str = lists:flatten(Str0),
- io:format("~s\n", [Str]),
- case Str of
- "internal"++_=Str ->
- ct:fail(internal_compiler_error);
- _ ->
- ok
- end.
-
--define(TC(Body), tc(fun() -> Body end, ?LINE)).
-
try_it(Module, Conf) ->
- %% Change 'false' to 'true' to start a new node for every module.
- try_it(false, Module, Conf).
-
-try_it(StartNode, Module, Conf) ->
- try_it(StartNode, Module, {minutes,10}, Conf).
-
-try_it(StartNode, Module, Timetrap, Conf) ->
+ Timetrap = {minutes,10},
OtherOpts = [], %Can be changed to [time] if needed
Src = filename:join(proplists:get_value(data_dir, Conf),
atom_to_list(Module)),
Out = proplists:get_value(priv_dir,Conf),
io:format("Compiling: ~s\n", [Src]),
- CompRc0 = compile:file(Src, [clint,{outdir,Out},report,
- bin_opt_info|OtherOpts]),
+ CompRc0 = compile:file(Src, [clint0,clint,{outdir,Out},report,
+ bin_opt_info|OtherOpts]),
io:format("Result: ~p\n",[CompRc0]),
{ok,_Mod} = CompRc0,
- Node = case StartNode of
- false ->
- node();
- true ->
- Pa = "-pa " ++ filename:dirname(code:which(?MODULE)),
- {ok,Node0} = start_node(compiler, Pa),
- Node0
- end,
-
- ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]),
load_and_call(Out, Module),
ct:timetrap(Timetrap),
io:format("Compiling (without optimization): ~s\n", [Src]),
CompRc1 = compile:file(Src,
- [no_copt,no_postopt,{outdir,Out},report|OtherOpts]),
+ [no_copt,no_postopt,
+ {outdir,Out},report|OtherOpts]),
io:format("Result: ~p\n",[CompRc1]),
{ok,_Mod} = CompRc1,
- ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]),
+ load_and_call(Out, Module),
ct:timetrap(Timetrap),
io:format("Compiling (with old inliner): ~s\n", [Src]),
- CompRc2 = compile:file(Src, [{outdir,Out},report,bin_opt_info,
- {inline,1000}|OtherOpts]),
+ CompRc2 = compile:file(Src, [clint,
+ {outdir,Out},report,bin_opt_info,
+ {inline,1000}|OtherOpts]),
io:format("Result: ~p\n",[CompRc2]),
{ok,_Mod} = CompRc2,
- ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]),
+ load_and_call(Out, Module),
ct:timetrap(Timetrap),
io:format("Compiling (from assembly): ~s\n", [Src]),
@@ -286,12 +203,8 @@ try_it(StartNode, Module, Timetrap, Conf) ->
CompRc3 = compile:file(Asm, [from_asm,{outdir,Out},report|OtherOpts]),
io:format("Result: ~p\n",[CompRc3]),
{ok,_} = CompRc3,
- ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]),
+ load_and_call(Out, Module),
- case StartNode of
- false -> ok;
- true -> test_server:stop_node(Node)
- end,
ok.
load_and_call(Out, Module) ->
@@ -319,24 +232,6 @@ load_and_call(Out, Module) ->
ok.
-tc(F, Line) ->
- {Diff,Value} = timer:tc(erlang, apply, [F,[]]),
- io:format("~p: ~p\n", [Line,Diff]),
- Value.
-
-start_node(Name, Args) ->
- case test_server:start_node(Name, slave, [{args, Args}]) of
- {ok, Node} ->
- {ok, Node};
- Error ->
- ct:fail(Error)
- end.
-
-from(H, [H | T]) -> T;
-from(H, [_ | T]) -> from(H, T);
-from(_, []) -> [].
-
-
%% Test generation of 'vsn' attribute.
vsn_1(Conf) when is_list(Conf) ->
M = vsn_1,
@@ -397,11 +292,6 @@ get_vsn(M) ->
{vsn,V} = lists:keyfind(vsn, 1, M:module_info(attributes)),
V.
-long_string(Config) when is_list(Config) ->
- %% The test must complete in one minute - it should be plenty of time.
- try_it(false, long_string, {minutes,1}, Config),
- ok.
-
compile_load(Module, Dir, Conf) ->
Src = filename:join(Dir, atom_to_list(Module)),
Out = proplists:get_value(priv_dir,Conf),
@@ -465,6 +355,7 @@ compile_compiler(Files, OutDir, Version, InlineOpts) ->
io:format("~ts", [code:which(compile)]),
io:format("Compiling ~s into ~ts", [Version,OutDir]),
Opts = [report,
+ clint0,clint,
bin_opt_info,
{outdir,OutDir},
{d,'COMPILER_VSN',"\""++Version++"\""},
@@ -480,10 +371,6 @@ compile_compiler(Files, OutDir, Version, InlineOpts) ->
compiler_src() ->
filelib:wildcard(filename:join([code:lib_dir(compiler), "src", "*.erl"])).
-compiler_modules(Dir) ->
- Files = filelib:wildcard(filename:join(Dir, "*.beam")),
- [list_to_atom(filename:rootname(filename:basename(F))) || F <- Files].
-
make_compiler_dir(Priv, Dir0) ->
Dir = filename:join(Priv, Dir0),
ok = file:make_dir(Dir),
@@ -502,112 +389,6 @@ compare_compilers(ADir, BDir) ->
["beam_asm.beam"] = [filename:basename(A) || {A,_} <- D],
ok.
-%%%
-%%% The only test of the following code is that it compiles.
-%%%
-
-%% Slightly simplifed from megaco_binary_term_id_gen.
-%% beam_block failed to note that the {gc_bif,'-'...} instruction could
-%% fail, and that therefore {y,0} need to be initialized.
-%% {allocate,8,6}.
-%% %% {init,{y,0}} needed here.
-%% {get_list,{x,1},{x,6},{x,7}}.
-%% {'catch',{y,7},{f,3}}.
-%% {move,{x,4},{y,1}}.
-%% {move,{x,3},{y,2}}.
-%% {move,{x,2},{y,3}}.
-%% {move,{x,5},{y,4}}.
-%% {move,{x,7},{y,5}}.
-%% {move,{x,6},{y,6}}.
-%% {gc_bif,'-',{f,0},8,[{x,3},{x,6}],{x,0}}.
-%% {move,{x,0},{y,0}}.
-
-encode_wildcards3([],[],_,_) -> [];
-encode_wildcards3([Level|Levels],[BitsInLevel|BitsRest],LevelNo,TotSize) ->
- case (catch ?MODULE:encode_wildcard(Level,BitsInLevel,TotSize-BitsInLevel,
- length(Levels))) of
- {'EXIT',{Reason,Info}} ->
- exit({Reason,{LevelNo,Info}});
-
- no_wildcard ->
- encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel);
-
- {level,Wl} ->
- [Wl|
- encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel)];
-
- {recursive,Wr} ->
- [Wr]
- end.
-
-%% Slightly simplified code from hipe_rtl_ssapre.
-%% beam_block used to do the following incorrect optimization:
-%%
-%% {gc_bif,length,{f,0},1,[{x,0}],{x,3}}.
-%% ^^^^^ Was {x,0} - changing to {x,3} is not safe.
-%% {gc_bif,'+',{f,0},0,[{y,2},{integer,1}],{x,0}}.
-%% ^^^ Only one register live
-%% . . .
-%% {call_last,4,{f,2},4}. %% beam_validator noted that {x,3} wasn't live.
-
-find_operands(Cfg,XsiGraph,[],_Count) ->
- {Cfg,XsiGraph};
-find_operands(Cfg,XsiGraph,ActiveList,Count) ->
- {NewCfg,TempActiveList}=?MODULE:find_operands_for_active_list(Cfg,XsiGraph,
- ActiveList,[]),
- NewActiveList=lists:reverse(TempActiveList),
- [Count+1, length(NewActiveList), length(digraph:vertices(XsiGraph))],
- find_operands(NewCfg,XsiGraph,NewActiveList,Count+1).
-
-
-%% The following code
-%%
-%% {get_list,{x,2},{x,0},{x,1}}.
-%% {gc_bif,length,{f,0},1,[{x,0}],{x,0}}.
-%% {move,{x,0},{x,1}}.
-%%
-%% was incorrectly optimized to
-%%
-%% {get_list,{x,2},{x,0},{y,0}}.
-%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}}.
-%%
-%% because beam_block:is_transparent({x,1},
-%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}}
-%% incorrectly returned true.
-
--record(contextId,{cid,device_type,contextRef}).
--record(dpRef,{cid,tlli,ms_device_context_id}).
--record(qosProfileBssgp,{peak_bit_rate_msb,
- peak_bit_rate_lsb,
- t_a_precedence}).
--record(llUnitdataReq,{sapi,
- l3_pdu_length,
- pdu_life}).
--record(ptmsi,{value}).
-
-otp_7345(Config) when is_list(Config) ->
- #llUnitdataReq{l3_pdu_length=3,pdu_life=4} =
- otp_7345(#contextId{}, 0, [[1,2,3],4,5]).
-
-
-otp_7345(ObjRef, _RdEnv, Args) ->
- Cid = ObjRef#contextId.cid,
- _ = #dpRef{cid = Cid,
- ms_device_context_id = cid_id,
- tlli = #ptmsi{value = 0}},
- _ = #qosProfileBssgp{peak_bit_rate_msb = 0,
- peak_bit_rate_lsb = 80,
- t_a_precedence = 49},
- [Cpdu|_] = Args,
- LlUnitdataReq =
- #llUnitdataReq{sapi = 7,
- l3_pdu_length = length(Cpdu),
- pdu_life =
- id(42)
- div
- 10},
- id(LlUnitdataReq).
-
%% Check the generation of the string table.
string_table(Config) when is_list(Config) ->
@@ -640,24 +421,6 @@ do_otp_8949_a() ->
end
end.
-otp_8949_b(Config) when is_list(Config) ->
- self() ! something,
- value = otp_8949_b([], false),
- {'EXIT',_} = (catch otp_8949_b([], true)),
- ok.
-
-%% Would cause an endless loop in beam_utils.
-otp_8949_b(A, B) ->
- Var = id(value),
- if
- A == [], B == false ->
- ok
- end,
- receive
- something ->
- id(Var)
- end.
-
split_cases(_) ->
dummy1 = do_split_cases(x),
{'EXIT',{{badmatch,b},_}} = (catch do_split_cases(y)),
@@ -673,21 +436,5 @@ do_split_cases(A) ->
end,
Z.
--record(alarmInfo, {type,cause,origin}).
-
-beam_utils_liveopt(Config) ->
- F = beam_utils_liveopt_fun(42, pebkac, user),
- void = F(42, #alarmInfo{type=sctp,cause=pebkac,origin=user}),
- ok.
-
-beam_utils_liveopt_fun(Peer, Cause, Origin) ->
- fun(PeerNo, AlarmInfo)
- when PeerNo == Peer andalso
- AlarmInfo == #alarmInfo{type=sctp,
- cause=Cause,
- origin=Origin} ->
- void
- end.
-
id(I) -> I.
diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_1.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_1.erl
deleted file mode 100644
index 3e2891a28d..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_1.erl
+++ /dev/null
@@ -1,32 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(beam_compiler_1).
--export([beam_compiler_1/0]).
-
-beam_compiler_1() ->
- ok.
-
--record(foo,{a,b}).
-
-try_me() ->
- X = #foo{},
- Y = #foo{},
- {X#foo.a == Y#foo.a,X#foo.b}.
-
diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_12.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_12.erl
deleted file mode 100644
index d34291159a..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_12.erl
+++ /dev/null
@@ -1,30 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(beam_compiler_12).
-
--export([?MODULE/0,t/1]).
-
-?MODULE() ->
- ok.
-
-t(Name) ->
- {ok = {file_info,_,regular,_,AccTime1,ModTime1,_,_,_,_,_,_,_,_}} =
- prim_file:read_file_info(Name).
-
diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_2.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_2.erl
deleted file mode 100644
index 473529bb58..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_2.erl
+++ /dev/null
@@ -1,36 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(beam_compiler_2).
--export([beam_compiler_2/0]).
-
-beam_compiler_2() ->
- ok.
-
--record(foo,{a,b}).
-
-try_me() ->
- try_me({foo,x,z},{foo,y,z}).
-
-try_me(X,Y) ->
- f(X#foo.a =/= Y#foo.a,X#foo.b =/= X#foo.b).
-
-f(A,B) ->
- A.
-
diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_3.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_3.erl
deleted file mode 100644
index bdc4ec06e5..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_3.erl
+++ /dev/null
@@ -1,30 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(beam_compiler_3).
--export([beam_compiler_3/0, f/0]).
-
-%% From Ulf Wiger.
-
-beam_compiler_3() ->
- ok.
-
-f() ->
- [_|T] = lists:reverse("xxx"),
- T.
diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_2.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_2.erl
deleted file mode 100644
index cd0c2a4b0e..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_2.erl
+++ /dev/null
@@ -1,42 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(bin_syntax_2).
-
--export([?MODULE/0]).
-
-%% This module tests that constant propagation is done properly.
-
-?MODULE() ->
- 258 = b(<<1,2>>),
- F = c(),
- 259 = F(<<1,3>>),
- ok.
-
-b(B) ->
- Sz = 16,
- <<X:Sz/integer>> = B,
- X.
-
-c() ->
- Size = 16,
- fun(Bin) ->
- <<X:Size/integer>> = Bin,
- X
- end.
diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_3.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_3.erl
deleted file mode 100644
index b3118e0adc..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_3.erl
+++ /dev/null
@@ -1,36 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(bin_syntax_3).
--export([?MODULE/0,decode_integer/3]).
-
-?MODULE() ->
- ok.
-
-decode_integer(Len, <<B1:1,B2:7,Bs/binary>>, RemovedBytes) when B1 == 0 ->
- Bin = <<Skip:Len/unit:8, Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>,
- Size = size(Bin),
- <<Int:Size/unit:8>> = Bin,
- {Int,Buffer2,RemovedBytes};
-decode_integer(Len,<<B1:1,B2:7,Bs/binary>>,RemovedBytes) ->
- Bin = <<Skip:Len/unit:8,Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>,
- Size = size(Bin),
- <<N:Size/unit:8>> = <<B2,Bs/binary>>,
- Int = N - (1 bsl (8 * size(Bin) -1)),
- {Int,Buffer2,RemovedBytes}.
diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_4.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_4.erl
deleted file mode 100644
index 185d6ec3b0..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_4.erl
+++ /dev/null
@@ -1,33 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(bin_syntax_4).
--export([?MODULE/0,f4b/2,f4c/2]).
-
-?MODULE() ->
- ok.
-
-f4b(X, Y) ->
- fun (<< A:Y >>, Y, B) -> fum(A, X, Y, B) end.
-
-f4c(X, Y) ->
- fun (Y, << A:Y >>, B) -> fum(A, X, Y, B) end.
-
-fum(A, B, C, D) ->
- {A,B,C,D}.
diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_6.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_6.erl
deleted file mode 100644
index 1841a2ee0a..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_6.erl
+++ /dev/null
@@ -1,40 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(bin_syntax_6).
--export([?MODULE/0,x/1,y/1]).
-
-?MODULE() ->
- ok.
-
-x(X) ->
- blurf(),
- B = {X,"OK",<<>>},
- catch b({a,B}).
-
-y(X) ->
- blurf(),
- B = {X,"OK",<<42>>},
- catch b({a,B}).
-
-blurf() ->
- ok.
-
-b(_) ->
- ok.
diff --git a/lib/compiler/test/compilation_SUITE_data/catch_in_catch.erl b/lib/compiler/test/compilation_SUITE_data/catch_in_catch.erl
deleted file mode 100644
index 05ef45f105..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/catch_in_catch.erl
+++ /dev/null
@@ -1,52 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(catch_in_catch).
-
--export([?MODULE/0,do_start/1]).
-
-?MODULE() ->
- process_flag(trap_exit, true),
- Pid = spawn_link(?MODULE, do_start, [x]),
- receive
- {'EXIT',Pid,good_exit} -> ok;
- Other ->
- io:format("Unexpected: ~p\n", [Other]),
- error
- after 32000 ->
- io:format("No message received\n"),
- error
- end.
-
-do_start(Param) ->
- init(Param),
- exit(good_exit).
-
-init(Param) ->
- process_flag(trap_exit, true),
- %% The catches were improperly nested, causing a "No catch found" crash.
- (catch begin
- foo(Param),
- (catch exit(bar))
- end
- ),
- ignore.
-
-foo(_) ->
- ok.
diff --git a/lib/compiler/test/compilation_SUITE_data/compiler_3.erl b/lib/compiler/test/compilation_SUITE_data/compiler_3.erl
deleted file mode 100644
index 698a0f26f3..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/compiler_3.erl
+++ /dev/null
@@ -1,34 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(compiler_3).
--export([compiler_3/0]).
--record(rec,{a}).
-
-compiler_3() ->
- guard_record().
-
-guard_record() ->
- 1=func(#rec{}),
- {'EXIT',_} = (catch func({rec})),
- ok.
-
-func(X) when record(X,
-rec) ->
- 1.
diff --git a/lib/compiler/test/compilation_SUITE_data/compiler_5.erl b/lib/compiler/test/compilation_SUITE_data/compiler_5.erl
deleted file mode 100644
index de3c2ec4ce..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/compiler_5.erl
+++ /dev/null
@@ -1,50 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(compiler_5).
--export([compiler_5/0]).
-
-compiler_5() ->
- f0(),
- f1(),
- f2(),
- ok.
-
-%% compiler treats records with 1 and 2 fields differently...
--record(nil, {}).
--record(foo, {hello=1}).
--record(bar, {hello=2,there=3}).
-
-f0() ->
- R1 = #nil{},
- R2 = R1#nil{}, %% stupid code, but compiler shouldn't crash
- R1 = R2,
- ok.
-
-f1() ->
- R1 = #foo{},
- R2 = R1#foo{}, %% stupid code, but compiler shouldn't crash
- R1 = R2,
- ok.
-
-f2() ->
- R1 = #bar{},
- R2 = R1#bar{}, %% stupid code, but compiler shouldn't crash
- R1 = R2,
- ok.
diff --git a/lib/compiler/test/compilation_SUITE_data/complex_guard.erl b/lib/compiler/test/compilation_SUITE_data/complex_guard.erl
deleted file mode 100644
index f1b88c2bd2..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/complex_guard.erl
+++ /dev/null
@@ -1,32 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(complex_guard).
-
--compile(export_all).
-
-?MODULE() ->
- ok.
-
-f(X1,Y1,Z1) ->
- if
- ((X1 =:= 4) or (X1 =:= 5)) and ((Y1 =:= 4) or (Y1 =:= 5)) and ((Z1 =:= 4) or (Z1 =:= 5)) or ((X1 =:= 1) or (X1 =:= 2) or (X1 =:= 3)) and ((Y1 =:= 1) or (Y1 =:= 2) or (Y1 =:= 3)) and ((Z1 =:= 1) or (Z1 =:= 2) or (Z1 =:= 3)) ->
- true
- end.
-
diff --git a/lib/compiler/test/compilation_SUITE_data/guards.erl b/lib/compiler/test/compilation_SUITE_data/guards.erl
deleted file mode 100644
index a507add790..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/guards.erl
+++ /dev/null
@@ -1,107 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(guards).
-
--export([guards/0]).
-
-guards() ->
- ok = t(),
- ok = f(),
- ok = ct(1),
- ok = multi(1),
- ok = multi(2),
- ok = multi(3).
-
-%% The following tests are always true.
-t() when integer(42) ->
- ok;
-t() when float(2.0) ->
- ok;
-t() when number(7) ->
- ok;
-t() when number(3.14) ->
- ok;
-t() when atom(error) ->
- ok;
-t() when list([a]) ->
- ok;
-t() when tuple({}) ->
- ok;
-t() when tuple({1, 2}) ->
- ok.
-
-%% The following tests are always false.
-f() when integer(a) ->
- ok;
-f() when float(b) ->
- ok;
-f() when number(c) ->
- ok;
-f() when atom(42) ->
- ok;
-f() when list(33) ->
- ok;
-f() when list({}) ->
- ok;
-f() when list({1, 2}) ->
- ok;
-f() when tuple(33) ->
- ok;
-f() when tuple([a]) ->
- ok;
-f() when tuple([]) ->
- ok;
-f() when tuple(35) ->
- ok;
-f() ->
- ok.
-
-%% The following tests are always true.
-ct(X) ->
- case X of
- Y when integer(42) ->
- ok;
- Y when float(2.0) ->
- ok;
- Y when number(7) ->
- ok;
- Y when number(3.14) ->
- ok;
- Y when atom(error) ->
- ok;
- Y when list([a]) ->
- ok;
- Y when tuple({}) ->
- ok;
- Y when tuple({1, 2}) ->
- ok
- end.
-
-multi(X) ->
- case X of
- Y when float(Y) ; integer(Y) ->
- ok;
- Y when Y > 1, Y < 10 ; atom(Y) ->
- ok;
- Y when Y == 4, number(Y) ; list(Y) ->
- pannkaka;
- Y when Y==3 ; Y==5 ; Y==6 ->
- ok
- end.
diff --git a/lib/compiler/test/compilation_SUITE_data/long_string.erl b/lib/compiler/test/compilation_SUITE_data/long_string.erl
deleted file mode 100644
index fe109c8e4f..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/long_string.erl
+++ /dev/null
@@ -1,671 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(long_string).
-
--export([?MODULE/0]).
-
-?MODULE() ->
- Options = "some stupid long string",
- 49252 = length(generate(Options, "348927432097sfkjfkljf329")),
- ok.
-
-generate(Options, Glurf) ->
- "asdhfaslfdjhhwleirsk e4kjhr430usduy fdk;///s llsjkf;laskjfsdfkjasdfkj
-sdkljflasdfkjasldkfjasd" ++ Options ++
-"CSAgICAgICBWZXJzaW9uIDIsIEp1bmUgMTk5MQoKIENvcHlyaWdodCAoQykgMTk4OSwgMTk5MSBG
-cmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIEluYy4KICAgICAgICAgICAgICAgICAgICAgICA1OSBU
-ZW1wbGUgUGxhY2UsIFN1aXRlIDMzMCwgQm9zdG9uLCBNQSAgMDIxMTEtMTMwNyAgVVNBCiBFdmVy
-eW9uZSBpcyBwZXJtaXR0ZWQgdG8gY29weSBhbmQgZGlzdHJpYnV0ZSB2ZXJiYXRpbSBjb3BpZXMK
-IG9mIHRoaXMgbGljZW5zZSBkb2N1bWVudCwgYnV0IGNoYW5naW5nIGl0IGlzIG5vdCBhbGxvd2Vk
-LgoKCQkJICAgIFByZWFtYmxlCgogIFRoZSBsaWNlbnNlcyBmb3IgbW9zdCBzb2Z0d2FyZSBhcmUg
-ZGVzaWduZWQgdG8gdGFrZSBhd2F5IHlvdXIKZnJlZWRvbSB0byBzaGFyZSBhbmQgY2hhbmdlIGl0
-LiAgQnkgY29udHJhc3QsIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMKTGljZW5zZSBpcyBpbnRlbmRl
-ZCB0byBndWFyYW50ZWUgeW91ciBmcmVlZG9tIHRvIHNoYXJlIGFuZCBjaGFuZ2UgZnJlZQpzb2Z0
-d2FyZS0tdG8gbWFrZSBzdXJlIHRoZSBzb2Z0d2FyZSBpcyBmcmVlIGZvciBhbGwgaXRzIHVzZXJz
-LiAgVGhpcwpHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFwcGxpZXMgdG8gbW9zdCBvZiB0aGUgRnJl
-ZSBTb2Z0d2FyZQpGb3VuZGF0aW9uJ3Mgc29mdHdhcmUgYW5kIHRvIGFueSBvdGhlciBwcm9ncmFt
-IHdob3NlIGF1dGhvcnMgY29tbWl0IHRvCnVzaW5nIGl0LiAgKFNvbWUgb3RoZXIgRnJlZSBTb2Z0
-d2FyZSBGb3VuZGF0aW9uIHNvZnR3YXJlIGlzIGNvdmVyZWQgYnkKdGhlIEdOVSBMaWJyYXJ5IEdl
-bmVyYWwgUHVibGljIExpY2Vuc2UgaW5zdGVhZC4pICBZb3UgY2FuIGFwcGx5IGl0IHRvCnlvdXIg
-cHJvZ3JhbXMsIHRvby4KCiAgV2hlbiB3ZSBzcGVhayBvZiBmcmVlIHNvZnR3YXJlLCB3ZSBhcmUg
-cmVmZXJyaW5nIHRvIGZyZWVkb20sIG5vdApwcmljZS4gIE91ciBHZW5lcmFsIFB1YmxpYyBMaWNl
-bnNlcyBhcmUgZGVzaWduZWQgdG8gbWFrZSBzdXJlIHRoYXQgeW91CmhhdmUgdGhlIGZyZWVkb20g
-dG8gZGlzdHJpYnV0ZSBjb3BpZXMgb2YgZnJlZSBzb2Z0d2FyZSAoYW5kIGNoYXJnZSBmb3IKdGhp
-cyBzZXJ2aWNlIGlmIHlvdSB3aXNoKSwgdGhhdCB5b3UgcmVjZWl2ZSBzb3VyY2UgY29kZSBvciBj
-YW4gZ2V0IGl0CmlmIHlvdSB3YW50IGl0LCB0aGF0IHlvdSBjYW4gY2hhbmdlIHRoZSBzb2Z0d2Fy
-ZSBvciB1c2UgcGllY2VzIG9mIGl0CmluIG5ldyBmcmVlIHByb2dyYW1zOyBhbmQgdGhhdCB5b3Ug
-a25vdyB5b3UgY2FuIGRvIHRoZXNlIHRoaW5ncy4KCiAgVG8gcHJvdGVjdCB5b3VyIHJpZ2h0cywg
-d2UgbmVlZCB0byBtYWtlIHJlc3RyaWN0aW9ucyB0aGF0IGZvcmJpZAphbnlvbmUgdG8gZGVueSB5
-b3UgdGhlc2UgcmlnaHRzIG9yIHRvIGFzayB5b3UgdG8gc3VycmVuZGVyIHRoZSByaWdodHMuClRo
-ZXNlIHJlc3RyaWN0aW9ucyB0cmFuc2xhdGUgdG8gY2VydGFpbiByZXNwb25zaWJpbGl0aWVzIGZv
-ciB5b3UgaWYgeW91CmRpc3RyaWJ1dGUgY29waWVzIG9mIHRoZSBzb2Z0d2FyZSwgb3IgaWYgeW91
-IG1vZGlmeSBpdC4KCiAgRm9yIGV4YW1wbGUsIGlmIHlvdSBkaXN0cmlidXRlIGNvcGllcyBvZiBz
-dWNoIGEgcHJvZ3JhbSwgd2hldGhlcgpncmF0aXMgb3IgZm9yIGEgZmVlLCB5b3UgbXVzdCBnaXZl
-IHRoZSByZWNpcGllbnRzIGFsbCB0aGUgcmlnaHRzIHRoYXQKeW91IGhhdmUuICBZb3UgbXVzdCBt
-YWtlIHN1cmUgdGhhdCB0aGV5LCB0b28sIHJlY2VpdmUgb3IgY2FuIGdldCB0aGUKc291cmNlIGNv
-ZGUuICBBbmQgeW91IG11c3Qgc2hvdyB0aGVtIHRoZXNlIHRlcm1zIHNvIHRoZXkga25vdyB0aGVp
-cgpyaWdodHMuCgogIFdlIHByb3RlY3QgeW91ciByaWdodHMgd2l0aCB0d28gc3RlcHM6ICgxKSBj
-b3B5cmlnaHQgdGhlIHNvZnR3YXJlLCBhbmQKKDIpIG9mZmVyIHlvdSB0aGlzIGxpY2Vuc2Ugd2hp
-Y2ggZ2l2ZXMgeW91IGxlZ2FsIHBlcm1pc3Npb24gdG8gY29weSwKZGlzdHJpYnV0ZSBhbmQvb3Ig
-bW9kaWZ5IHRoZSBzb2Z0d2FyZS4KCiAgQWxzbywgZm9yIGVhY2ggYXV0aG9yJ3MgcHJvdGVjdGlv
-biBhbmQgb3Vycywgd2Ugd2FudCB0byBtYWtlIGNlcnRhaW4KdGhhdCBldmVyeW9uZSB1bmRlcnN0
-YW5kcyB0aGF0IHRoZXJlIGlzIG5vIHdhcnJhbnR5IGZvciB0aGlzIGZyZWUKc29mdHdhcmUuICBJ
-ZiB0aGUgc29mdHdhcmUgaXMgbW9kaWZpZWQgYnkgc29tZW9uZSBlbHNlIGFuZCBwYXNzZWQgb24s
-IHdlCndhbnQgaXRzIHJlY2lwaWVudHMgdG8ga25vdyB0aGF0IHdoYXQgdGhleSBoYXZlIGlzIG5v
-dCB0aGUgb3JpZ2luYWwsIHNvCnRoYXQgYW55IHByb2JsZW1zIGludHJvZHVjZWQgYnkgb3RoZXJz
-IHdpbGwgbm90IHJlZmxlY3Qgb24gdGhlIG9yaWdpbmFsCmF1dGhvcnMnIHJlcHV0YXRpb25zLgoK
-ICBGaW5hbGx5LCBhbnkgZnJlZSBwcm9ncmFtIGlzIHRocmVhdGVuZWQgY29uc3RhbnRseSBieSBz
-b2Z0d2FyZQpwYXRlbnRzLiAgV2Ugd2lzaCB0byBhdm9pZCB0aGUgZGFuZ2VyIHRoYXQgcmVkaXN0
-cmlidXRvcnMgb2YgYSBmcmVlCnByb2dyYW0gd2lsbCBpbmRpdmlkdWFsbHkgb2J0YWluIHBhdGVu
-dCBsaWNlbnNlcywgaW4gZWZmZWN0IG1ha2luZyB0aGUKcHJvZ3JhbSBwcm9wcmlldGFyeS4gIFRv
-IHByZXZlbnQgdGhpcywgd2UgaGF2ZSBtYWRlIGl0IGNsZWFyIHRoYXQgYW55CnBhdGVudCBtdXN0
-IGJlIGxpY2Vuc2VkIGZvciBldmVyeW9uZSdzIGZyZWUgdXNlIG9yIG5vdCBsaWNlbnNlZCBhdCBh
-bGwuCgogIFRoZSBwcmVjaXNlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciBjb3B5aW5nLCBkaXN0
-cmlidXRpb24gYW5kCm1vZGlmaWNhdGlvbiBmb2xsb3cuCgwKCQkgICAgR05VIEdFTkVSQUwgUFVC
-TElDIExJQ0VOU0UKICAgVEVSTVMgQU5EIENPTkRJVElPTlMgRk9SIENPUFlJTkcsIERJU1RSSUJV
-VElPTiBBTkQgTU9ESUZJQ0FUSU9OCgogIDAuIFRoaXMgTGljZW5zZSBhcHBsaWVzIHRvIGFueSBw
-cm9ncmFtIG9yIG90aGVyIHdvcmsgd2hpY2ggY29udGFpbnMKYSBub3RpY2UgcGxhY2VkIGJ5IHRo
-ZSBjb3B5cmlnaHQgaG9sZGVyIHNheWluZyBpdCBtYXkgYmUgZGlzdHJpYnV0ZWQKdW5kZXIgdGhl
-IHRlcm1zIG9mIHRoaXMgR2VuZXJhbCBQdWJsaWMgTGljZW5zZS4gIFRoZSAiUHJvZ3JhbSIsIGJl
-bG93LApyZWZlcnMgdG8gYW55IHN1Y2ggcHJvZ3JhbSBvciB3b3JrLCBhbmQgYSAid29yayBiYXNl
-ZCBvbiB0aGUgUHJvZ3JhbSIKbWVhbnMgZWl0aGVyIHRoZSBQcm9ncmFtIG9yIGFueSBkZXJpdmF0
-aXZlIHdvcmsgdW5kZXIgY29weXJpZ2h0IGxhdzoKdGhhdCBpcyB0byBzYXksIGEgd29yayBjb250
-YWluaW5nIHRoZSBQcm9ncmFtIG9yIGEgcG9ydGlvbiBvZiBpdCwKZWl0aGVyIHZlcmJhdGltIG9y
-IHdpdGggbW9kaWZpY2F0aW9ucyBhbmQvb3IgdHJhbnNsYXRlZCBpbnRvIGFub3RoZXIKbGFuZ3Vh
-Z2UuICAoSGVyZWluYWZ0ZXIsIHRyYW5zbGF0aW9uIGlzIGluY2x1ZGVkIHdpdGhvdXQgbGltaXRh
-dGlvbiBpbgp0aGUgdGVybSAibW9kaWZpY2F0aW9uIi4pICBFYWNoIGxpY2Vuc2VlIGlzIGFkZHJl
-c3NlZCBhcyAieW91Ii4KCkFjdGl2aXRpZXMgb3RoZXIgdGhhbiBjb3B5aW5nLCBkaXN0cmlidXRp
-b24gYW5kIG1vZGlmaWNhdGlvbiBhcmUgbm90CmNvdmVyZWQgYnkgdGhpcyBMaWNlbnNlOyB0aGV5
-IGFyZSBvdXRzaWRlIGl0cyBzY29wZS4gIFRoZSBhY3Qgb2YKcnVubmluZyB0aGUgUHJvZ3JhbSBp
-cyBub3QgcmVzdHJpY3RlZCwgYW5kIHRoZSBvdXRwdXQgZnJvbSB0aGUgUHJvZ3JhbQppcyBjb3Zl
-cmVkIG9ubHkgaWYgaXRzIGNvbnRlbnRzIGNvbnN0aXR1dGUgYSB3b3JrIGJhc2VkIG9uIHRoZQpQ
-cm9ncmFtIChpbmRlcGVuZGVudCBvZiBoYXZpbmcgYmVlbiBtYWRlIGJ5IHJ1bm5pbmcgdGhlIFBy
-b2dyYW0pLgpXaGV0aGVyIHRoYXQgaXMgdHJ1ZSBkZXBlbmRzIG9uIHdoYXQgdGhlIFByb2dyYW0g
-ZG9lcy4KCiAgMS4gWW91IG1heSBjb3B5IGFuZCBkaXN0cmlidXRlIHZlcmJhdGltIGNvcGllcyBv
-ZiB0aGUgUHJvZ3JhbSdzCnNvdXJjZSBjb2RlIGFzIHlvdSByZWNlaXZlIGl0LCBpbiBhbnkgbWVk
-aXVtLCBwcm92aWRlZCB0aGF0IHlvdQpjb25zcGljdW91c2x5IGFuZCBhcHByb3ByaWF0ZWx5IHB1
-Ymxpc2ggb24gZWFjaCBjb3B5IGFuIGFwcHJvcHJpYXRlCmNvcHlyaWdodCBub3RpY2UgYW5kIGRp
-c2NsYWltZXIgb2Ygd2FycmFudHk7IGtlZXAgaW50YWN0IGFsbCB0aGUKbm90aWNlcyB0aGF0IHJl
-ZmVyIHRvIHRoaXMgTGljZW5zZSBhbmQgdG8gdGhlIGFic2VuY2Ugb2YgYW55IHdhcnJhbnR5Owph
-bmQgZ2l2ZSBhbnkgb3RoZXIgcmVjaXBpZW50cyBvZiB0aGUgUHJvZ3JhbSBhIGNvcHkgb2YgdGhp
-cyBMaWNlbnNlCmFsb25nIHdpdGggdGhlIFByb2dyYW0uCgpZb3UgbWF5IGNoYXJnZSBhIGZlZSBm
-b3IgdGhlIHBoeXNpY2FsIGFjdCBvZiB0cmFuc2ZlcnJpbmcgYSBjb3B5LCBhbmQKeW91IG1heSBh
-dCB5b3VyIG9wdGlvbiBvZmZlciB3YXJyYW50eSBwcm90ZWN0aW9uIGluIGV4Y2hhbmdlIGZvciBh
-IGZlZS4KCiAgMi4gWW91IG1heSBtb2RpZnkgeW91ciBjb3B5IG9yIGNvcGllcyBvZiB0aGUgUHJv
-Z3JhbSBvciBhbnkgcG9ydGlvbgpvZiBpdCwgdGh1cyBmb3JtaW5nIGEgd29yayBiYXNlZCBvbiB0
-aGUgUHJvZ3JhbSwgYW5kIGNvcHkgYW5kCmRpc3RyaWJ1dGUgc3VjaCBtb2RpZmljYXRpb25zIG9y
-IHdvcmsgdW5kZXIgdGhlIHRlcm1zIG9mIFNlY3Rpb24gMQphYm92ZSwgcHJvdmlkZWQgdGhhdCB5
-b3UgYWxzbyBtZWV0IGFsbCBvZiB0aGVzZSBjb25kaXRpb25zOgoKICAgIGEpIFlvdSBtdXN0IGNh
-dXNlIHRoZSBtb2RpZmllZCBmaWxlcyB0byBjYXJyeSBwcm9taW5lbnQgbm90aWNlcwogICAgc3Rh
-dGluZyB0aGF0IHlvdSBjaGFuZ2VkIHRoZSBmaWxlcyBhbmQgdGhlIGRhdGUgb2YgYW55IGNoYW5n
-ZS4KCiAgICBiKSBZb3UgbXVzdCBjYXVzZSBhbnkgd29yayB0aGF0IHlvdSBkaXN0cmlidXRlIG9y
-IHB1Ymxpc2gsIHRoYXQgaW4KICAgIHdob2xlIG9yIGluIHBhcnQgY29udGFpbnMgb3IgaXMgZGVy
-aXZlZCBmcm9tIHRoZSBQcm9ncmFtIG9yIGFueQogICAgcGFydCB0aGVyZW9mLCB0byBiZSBsaWNl
-bnNlZCBhcyBhIHdob2xlIGF0IG5vIGNoYXJnZSB0byBhbGwgdGhpcmQKICAgIHBhcnRpZXMgdW5k
-ZXIgdGhlIHRlcm1zIG9mIHRoaXMgTGljZW5zZS4KCiAgICBjKSBJZiB0aGUgbW9kaWZpZWQgcHJv
-Z3JhbSBub3JtYWxseSByZWFkcyBjb21tYW5kcyBpbnRlcmFjdGl2ZWx5CiAgICB3aGVuIHJ1biwg
-eW91IG11c3QgY2F1c2UgaXQsIHdoZW4gc3RhcnRlZCBydW5uaW5nIGZvciBzdWNoCiAgICBpbnRl
-cmFjdGl2ZSB1c2UgaW4gdGhlIG1vc3Qgb3JkaW5hcnkgd2F5LCB0byBwcmludCBvciBkaXNwbGF5
-IGFuCiAgICBhbm5vdW5jZW1lbnQgaW5jbHVkaW5nIGFuIGFwcHJvcHJpYXRlIGNvcHlyaWdodCBu
-b3RpY2UgYW5kIGEKICAgIG5vdGljZSB0aGF0IHRoZXJlIGlzIG5vIHdhcnJhbnR5IChvciBlbHNl
-LCBzYXlpbmcgdGhhdCB5b3UgcHJvdmlkZQogICAgYSB3YXJyYW50eSkgYW5kIHRoYXQgdXNlcnMg
-bWF5IHJlZGlzdHJpYnV0ZSB0aGUgcHJvZ3JhbSB1bmRlcgogICAgdGhlc2UgY29uZGl0aW9ucywg
-YW5kIHRlbGxpbmcgdGhlIHVzZXIgaG93IHRvIHZpZXcgYSBjb3B5IG9mIHRoaXMKICAgIExpY2Vu
-c2UuICAoRXhjZXB0aW9uOiBpZiB0aGUgUHJvZ3JhbSBpdHNlbGYgaXMgaW50ZXJhY3RpdmUgYnV0
-CiAgICBkb2VzIG5vdCBub3JtYWxseSBwcmludCBzdWNoIGFuIGFubm91bmNlbWVudCwgeW91ciB3
-b3JrIGJhc2VkIG9uCiAgICB0aGUgUHJvZ3JhbSBpcyBub3QgcmVxdWlyZWQgdG8gcHJpbnQgYW4g
-YW5ub3VuY2VtZW50LikKDApUaGVzZSByZXF1aXJlbWVudHMgYXBwbHkgdG8gdGhlIG1vZGlmaWVk
-IHdvcmsgYXMgYSB3aG9sZS4gIElmCmlkZW50aWZpYWJsZSBzZWN0aW9ucyBvZiB0aGF0IHdvcmsg
-YXJlIG5vdCBkZXJpdmVkIGZyb20gdGhlIFByb2dyYW0sCmFuZCBjYW4gYmUgcmVhc29uYWJseSBj
-b25zaWRlcmVkIGluZGVwZW5kZW50IGFuZCBzZXBhcmF0ZSB3b3JrcyBpbgp0aGVtc2VsdmVzLCB0
-aGVuIHRoaXMgTGljZW5zZSwgYW5kIGl0cyB0ZXJtcywgZG8gbm90IGFwcGx5IHRvIHRob3NlCnNl
-Y3Rpb25zIHdoZW4geW91IGRpc3RyaWJ1dGUgdGhlbSBhcyBzZXBhcmF0ZSB3b3Jrcy4gIEJ1dCB3
-aGVuIHlvdQpkaXN0cmlidXRlIHRoZSBzYW1lIHNlY3Rpb25zIGFzIHBhcnQgb2YgYSB3aG9sZSB3
-aGljaCBpcyBhIHdvcmsgYmFzZWQKb24gdGhlIFByb2dyYW0sIHRoZSBkaXN0cmlidXRpb24gb2Yg
-dGhlIHdob2xlIG11c3QgYmUgb24gdGhlIHRlcm1zIG9mCnRoaXMgTGljZW5zZSwgd2hvc2UgcGVy
-bWlzc2lvbnMgZm9yIG90aGVyIGxpY2Vuc2VlcyBleHRlbmQgdG8gdGhlCmVudGlyZSB3aG9sZSwg
-YW5kIHRodXMgdG8gZWFjaCBhbmQgZXZlcnkgcGFydCByZWdhcmRsZXNzIG9mIHdobyB3cm90ZSBp
-dC4KClRodXMsIGl0IGlzIG5vdCB0aGUgaW50ZW50IG9mIHRoaXMgc2VjdGlvbiB0byBjbGFpbSBy
-aWdodHMgb3IgY29udGVzdAp5b3VyIHJpZ2h0cyB0byB3b3JrIHdyaXR0ZW4gZW50aXJlbHkgYnkg
-eW91OyByYXRoZXIsIHRoZSBpbnRlbnQgaXMgdG8KZXhlcmNpc2UgdGhlIHJpZ2h0IHRvIGNvbnRy
-b2wgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkZXJpdmF0aXZlIG9yCmNvbGxlY3RpdmUgd29ya3MgYmFz
-ZWQgb24gdGhlIFByb2dyYW0uCgpJbiBhZGRpdGlvbiwgbWVyZSBhZ2dyZWdhdGlvbiBvZiBhbm90
-aGVyIHdvcmsgbm90IGJhc2VkIG9uIHRoZSBQcm9ncmFtCndpdGggdGhlIFByb2dyYW0gKG9yIHdp
-dGggYSB3b3JrIGJhc2VkIG9uIHRoZSBQcm9ncmFtKSBvbiBhIHZvbHVtZSBvZgphIHN0b3JhZ2Ug
-b3IgZGlzdHJpYnV0aW9uIG1lZGl1bSBkb2VzIG5vdCBicmluZyB0aGUgb3RoZXIgd29yayB1bmRl
-cgp0aGUgc2NvcGUgb2YgdGhpcyBMaWNlbnNlLgoKICAzLiBZb3UgbWF5IGNvcHkgYW5kIGRpc3Ry
-aWJ1dGUgdGhlIFByb2dyYW0gKG9yIGEgd29yayBiYXNlZCBvbiBpdCwKdW5kZXIgU2VjdGlvbiAy
-KSBpbiBvYmplY3QgY29kZSBvciBleGVjdXRhYmxlIGZvcm0gdW5kZXIgdGhlIHRlcm1zIG9mClNl
-Y3Rpb25zIDEgYW5kIDIgYWJvdmUgcHJvdmlkZWQgdGhhdCB5b3UgYWxzbyBkbyBvbmUgb2YgdGhl
-IGZvbGxvd2luZzoKCiAgICBhKSBBY2NvbXBhbnkgaXQgd2l0aCB0aGUgY29tcGxldGUgY29ycmVz
-cG9uZGluZyBtYWNoaW5lLXJlYWRhYmxlCiAgICBzb3VyY2UgY29kZSwgd2hpY2ggbXVzdCBiZSBk
-aXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgU2VjdGlvbnMKICAgIDEgYW5kIDIgYWJvdmUg
-b24gYSBtZWRpdW0gY3VzdG9tYXJpbHkgdXNlZCBmb3Igc29mdHdhcmUgaW50ZXJjaGFuZ2U7IG9y
-LAoKICAgIGIpIEFjY29tcGFueSBpdCB3aXRoIGEgd3JpdHRlbiBvZmZlciwgdmFsaWQgZm9yIGF0
-IGxlYXN0IHRocmVlCiAgICB5ZWFycywgdG8gZ2l2ZSBhbnkgdGhpcmQgcGFydHksIGZvciBhIGNo
-YXJnZSBubyBtb3JlIHRoYW4geW91cgogICAgY29zdCBvZiBwaHlzaWNhbGx5IHBlcmZvcm1pbmcg
-c291cmNlIGRpc3RyaWJ1dGlvbiwgYSBjb21wbGV0ZQogICAgbWFjaGluZS1yZWFkYWJsZSBjb3B5
-IG9mIHRoZSBjb3JyZXNwb25kaW5nIHNvdXJjZSBjb2RlLCB0byBiZQogICAgZGlzdHJpYnV0ZWQg
-dW5kZXIgdGhlIHRlcm1zIG9mIFNlY3Rpb25zIDEgYW5kIDIgYWJvdmUgb24gYSBtZWRpdW0KICAg
-IGN1c3RvbWFyaWx5IHVzZWQgZm9yIHNvZnR3YXJlIGludGVyY2hhbmdlOyBvciwKCiAgICBjKSBB
-Y2NvbXBhbnkgaXQgd2l0aCB0aGUgaW5mb3JtYXRpb24geW91IHJlY2VpdmVkIGFzIHRvIHRoZSBv
-ZmZlcgogICAgdG8gZGlzdHJpYnV0ZSBjb3JyZXNwb25kaW5nIHNvdXJjZSBjb2RlLiAgKFRoaXMg
-YWx0ZXJuYXRpdmUgaXMKICAgIGFsbG93ZWQgb25seSBmb3Igbm9uY29tbWVyY2lhbCBkaXN0cmli
-dXRpb24gYW5kIG9ubHkgaWYgeW91CiAgICByZWNlaXZlZCB0aGUgcHJvZ3JhbSBpbiBvYmplY3Qg
-Y29kZSBvciBleGVjdXRhYmxlIGZvcm0gd2l0aCBzdWNoCiAgICBhbiBvZmZlciwgaW4gYWNjb3Jk
-IHdpdGggU3Vic2VjdGlvbiBiIGFib3ZlLikKClRoZSBzb3VyY2UgY29kZSBmb3IgYSB3b3JrIG1l
-YW5zIHRoZSBwcmVmZXJyZWQgZm9ybSBvZiB0aGUgd29yayBmb3IKbWFraW5nIG1vZGlmaWNhdGlv
-bnMgdG8gaXQuICBGb3IgYW4gZXhlY3V0YWJsZSB3b3JrLCBjb21wbGV0ZSBzb3VyY2UKY29kZSBt
-ZWFucyBhbGwgdGhlIHNvdXJjZSBjb2RlIGZvciBhbGwgbW9kdWxlcyBpdCBjb250YWlucywgcGx1
-cyBhbnkKYXNzb2NpYXRlZCBpbnRlcmZhY2UgZGVmaW5pdGlvbiBmaWxlcywgcGx1cyB0aGUgc2Ny
-aXB0cyB1c2VkIHRvCmNvbnRyb2wgY29tcGlsYXRpb24gYW5kIGluc3RhbGxhdGlvbiBvZiB0aGUg
-ZXhlY3V0YWJsZS4gIEhvd2V2ZXIsIGFzIGEKc3BlY2lhbCBleGNlcHRpb24sIHRoZSBzb3VyY2Ug
-Y29kZSBkaXN0cmlidXRlZCBuZWVkIG5vdCBpbmNsdWRlCmFueXRoaW5nIHRoYXQgaXMgbm9ybWFs
-bHkgZGlzdHJpYnV0ZWQgKGluIGVpdGhlciBzb3VyY2Ugb3IgYmluYXJ5CmZvcm0pIHdpdGggdGhl
-IG1ham9yIGNvbXBvbmVudHMgKGNvbXBpbGVyLCBrZXJuZWwsIGFuZCBzbyBvbikgb2YgdGhlCm9w
-ZXJhdGluZyBzeXN0ZW0gb24gd2hpY2ggdGhlIGV4ZWN1dGFibGUgcnVucywgdW5sZXNzIHRoYXQg
-Y29tcG9uZW50Cml0c2VsZiBhY2NvbXBhbmllcyB0aGUgZXhlY3V0YWJsZS4KCklmIGRpc3RyaWJ1
-dGlvbiBvZiBleGVjdXRhYmxlIG9yIG9iamVjdCBjb2RlIGlzIG1hZGUgYnkgb2ZmZXJpbmcKYWNj
-ZXNzIHRvIGNvcHkgZnJvbSBhIGRlc2lnbmF0ZWQgcGxhY2UsIHRoZW4gb2ZmZXJpbmcgZXF1aXZh
-bGVudAphY2Nlc3MgdG8gY29weSB0aGUgc291cmNlIGNvZGUgZnJvbSB0aGUgc2FtZSBwbGFjZSBj
-b3VudHMgYXMKZGlzdHJpYnV0aW9uIG9mIHRoZSBzb3VyY2UgY29kZSwgZXZlbiB0aG91Z2ggdGhp
-cmQgcGFydGllcyBhcmUgbm90CmNvbXBlbGxlZCB0byBjb3B5IHRoZSBzb3VyY2UgYWxvbmcgd2l0
-aCB0aGUgb2JqZWN0IGNvZGUuCgwKICA0LiBZb3UgbWF5IG5vdCBjb3B5LCBtb2RpZnksIHN1Ymxp
-Y2Vuc2UsIG9yIGRpc3RyaWJ1dGUgdGhlIFByb2dyYW0KZXhjZXB0IGFzIGV4cHJlc3NseSBwcm92
-aWRlZCB1bmRlciB0aGlzIExpY2Vuc2UuICBBbnkgYXR0ZW1wdApvdGhlcndpc2UgdG8gY29weSwg
-bW9kaWZ5LCBzdWJsaWNlbnNlIG9yIGRpc3RyaWJ1dGUgdGhlIFByb2dyYW0gaXMKdm9pZCwgYW5k
-IHdpbGwgYXV0b21hdGljYWxseSB0ZXJtaW5hdGUgeW91ciByaWdodHMgdW5kZXIgdGhpcyBMaWNl
-bnNlLgpIb3dldmVyLCBwYXJ0aWVzIHdobyBoYXZlIHJlY2VpdmVkIGNvcGllcywgb3IgcmlnaHRz
-LCBmcm9tIHlvdSB1bmRlcgp0aGlzIExpY2Vuc2Ugd2lsbCBub3QgaGF2ZSB0aGVpciBsaWNlbnNl
-cyB0ZXJtaW5hdGVkIHNvIGxvbmcgYXMgc3VjaApwYXJ0aWVzIHJlbWFpbiBpbiBmdWxsIGNvbXBs
-aWFuY2UuCgogIDUuIFlvdSBhcmUgbm90IHJlcXVpcmVkIHRvIGFjY2VwdCB0aGlzIExpY2Vuc2Us
-IHNpbmNlIHlvdSBoYXZlIG5vdApzaWduZWQgaXQuICBIb3dldmVyLCBub3RoaW5nIGVsc2UgZ3Jh
-bnRzIHlvdSBwZXJtaXNzaW9uIHRvIG1vZGlmeSBvcgpkaXN0cmlidXRlIHRoZSBQcm9ncmFtIG9y
-IGl0cyBkZXJpdmF0aXZlIHdvcmtzLiAgVGhlc2UgYWN0aW9ucyBhcmUKcHJvaGliaXRlZCBieSBs
-YXcgaWYgeW91IGRvIG5vdCBhY2NlcHQgdGhpcyBMaWNlbnNlLiAgVGhlcmVmb3JlLCBieQptb2Rp
-Znlpbmcgb3IgZGlzdHJpYnV0aW5nIHRoZSBQcm9ncmFtIChvciBhbnkgd29yayBiYXNlZCBvbiB0
-aGUKUHJvZ3JhbSksIHlvdSBpbmRpY2F0ZSB5b3VyIGFjY2VwdGFuY2Ugb2YgdGhpcyBMaWNlbnNl
-IHRvIGRvIHNvLCBhbmQKYWxsIGl0cyB0ZXJtcyBhbmQgY29uZGl0aW9ucyBmb3IgY29weWluZywg
-ZGlzdHJpYnV0aW5nIG9yIG1vZGlmeWluZwp0aGUgUHJvZ3JhbSBvciB3b3JrcyBiYXNlZCBvbiBp
-dC4KCiAgNi4gRWFjaCB0aW1lIHlvdSByZWRpc3RyaWJ1dGUgdGhlIFByb2dyYW0gKG9yIGFueSB3
-b3JrIGJhc2VkIG9uIHRoZQpQcm9ncmFtKSwgdGhlIHJlY2lwaWVudCBhdXRvbWF0aWNhbGx5IHJl
-Y2VpdmVzIGEgbGljZW5zZSBmcm9tIHRoZQpvcmlnaW5hbCBsaWNlbnNvciB0byBjb3B5LCBkaXN0
-cmlidXRlIG9yIG1vZGlmeSB0aGUgUHJvZ3JhbSBzdWJqZWN0IHRvCnRoZXNlIHRlcm1zIGFuZCBj
-b25kaXRpb25zLiAgWW91IG1heSBub3QgaW1wb3NlIGFueSBmdXJ0aGVyCnJlc3RyaWN0aW9ucyBv
-biB0aGUgcmVjaXBpZW50cycgZXhlcmNpc2Ugb2YgdGhlIHJpZ2h0cyBncmFudGVkIGhlcmVpbi4K
-WW91IGFyZSBub3QgcmVzcG9uc2libGUgZm9yIGVuZm9yY2luZyBjb21wbGlhbmNlIGJ5IHRoaXJk
-IHBhcnRpZXMgdG8KdGhpcyBMaWNlbnNlLgoKICA3LiBJZiwgYXMgYSBjb25zZXF1ZW5jZSBvZiBh
-IGNvdXJ0IGp1ZGdtZW50IG9yIGFsbGVnYXRpb24gb2YgcGF0ZW50CmluZnJpbmdlbWVudCBvciBm
-b3IgYW55IG90aGVyIHJlYXNvbiAobm90IGxpbWl0ZWQgdG8gcGF0ZW50IGlzc3VlcyksCmNvbmRp
-dGlvbnMgYXJlIGltcG9zZWQgb24geW91ICh3aGV0aGVyIGJ5IGNvdXJ0IG9yZGVyLCBhZ3JlZW1l
-bnQgb3IKb3RoZXJ3aXNlKSB0aGF0IGNvbnRyYWRpY3QgdGhlIGNvbmRpdGlvbnMgb2YgdGhpcyBM
-aWNlbnNlLCB0aGV5IGRvIG5vdApleGN1c2UgeW91IGZyb20gdGhlIGNvbmRpdGlvbnMgb2YgdGhp
-cyBMaWNlbnNlLiAgSWYgeW91IGNhbm5vdApkaXN0cmlidXRlIHNvIGFzIHRvIHNhdGlzZnkgc2lt
-dWx0YW5lb3VzbHkgeW91ciBvYmxpZ2F0aW9ucyB1bmRlciB0aGlzCkxpY2Vuc2UgYW5kIGFueSBv
-dGhlciBwZXJ0aW5lbnQgb2JsaWdhdGlvbnMsIHRoZW4gYXMgYSBjb25zZXF1ZW5jZSB5b3UKbWF5
-IG5vdCBkaXN0cmlidXRlIHRoZSBQcm9ncmFtIGF0IGFsbC4gIEZvciBleGFtcGxlLCBpZiBhIHBh
-dGVudApsaWNlbnNlIHdvdWxkIG5vdCBwZXJtaXQgcm95YWx0eS1mcmVlIHJlZGlzdHJpYnV0aW9u
-IG9mIHRoZSBQcm9ncmFtIGJ5CmFsbCB0aG9zZSB3aG8gcmVjZWl2ZSBjb3BpZXMgZGlyZWN0bHkg
-b3IgaW5kaXJlY3RseSB0aHJvdWdoIHlvdSwgdGhlbgp0aGUgb25seSB3YXkgeW91IGNvdWxkIHNh
-dGlzZnkgYm90aCBpdCBhbmQgdGhpcyBMaWNlbnNlIHdvdWxkIGJlIHRvCnJlZnJhaW4gZW50aXJl
-bHkgZnJvbSBkaXN0cmlidXRpb24gb2YgdGhlIFByb2dyYW0uCgpJZiBhbnkgcG9ydGlvbiBvZiB0
-aGlzIHNlY3Rpb24gaXMgaGVsZCBpbnZhbGlkIG9yIHVuZW5mb3JjZWFibGUgdW5kZXIKYW55IHBh
-cnRpY3VsYXIgY2lyY3Vtc3RhbmNlLCB0aGUgYmFsYW5jZSBvZiB0aGUgc2VjdGlvbiBpcyBpbnRl
-bmRlZCB0bwphcHBseSBhbmQgdGhlIHNlY3Rpb24gYXMgYSB3aG9sZSBpcyBpbnRlbmRlZCB0byBh
-cHBseSBpbiBvdGhlcgpjaXJjdW1zdGFuY2VzLgoKSXQgaXMgbm90IHRoZSBwdXJwb3NlIG9mIHRo
-aXMgc2VjdGlvbiB0byBpbmR1Y2UgeW91IHRvIGluZnJpbmdlIGFueQpwYXRlbnRzIG9yIG90aGVy
-IHByb3BlcnR5IHJpZ2h0IGNsYWltcyBvciB0byBjb250ZXN0IHZhbGlkaXR5IG9mIGFueQpzdWNo
-IGNsYWltczsgdGhpcyBzZWN0aW9uIGhhcyB0aGUgc29sZSBwdXJwb3NlIG9mIHByb3RlY3Rpbmcg
-dGhlCmludGVncml0eSBvZiB0aGUgZnJlZSBzb2Z0d2FyZSBkaXN0cmlidXRpb24gc3lzdGVtLCB3
-aGljaCBpcwppbXBsZW1lbnRlZCBieSBwdWJsaWMgbGljZW5zZSBwcmFjdGljZXMuICBNYW55IHBl
-b3BsZSBoYXZlIG1hZGUKZ2VuZXJvdXMgY29udHJpYnV0aW9ucyB0byB0aGUgd2lkZSByYW5nZSBv
-ZiBzb2Z0d2FyZSBkaXN0cmlidXRlZAp0aHJvdWdoIHRoYXQgc3lzdGVtIGluIHJlbGlhbmNlIG9u
-IGNvbnNpc3RlbnQgYXBwbGljYXRpb24gb2YgdGhhdApzeXN0ZW07IGl0IGlzIHVwIHRvIHRoZSBh
-dXRob3IvZG9ub3IgdG8gZGVjaWRlIGlmIGhlIG9yIHNoZSBpcyB3aWxsaW5nCnRvIGRpc3RyaWJ1
-dGUgc29mdHdhcmUgdGhyb3VnaCBhbnkgb3RoZXIgc3lzdGVtIGFuZCBhIGxpY2Vuc2VlIGNhbm5v
-dAppbXBvc2UgdGhhdCBjaG9pY2UuCgpUaGlzIHNlY3Rpb24gaXMgaW50ZW5kZWQgdG8gbWFrZSB0
-aG9yb3VnaGx5IGNsZWFyIHdoYXQgaXMgYmVsaWV2ZWQgdG8KYmUgYSBjb25zZXF1ZW5jZSBvZiB0
-aGUgcmVzdCBvZiB0aGlzIExpY2Vuc2UuCgwKICA4LiBJZiB0aGUgZGlzdHJpYnV0aW9uIGFuZC9v
-ciB1c2Ugb2YgdGhlIFByb2dyYW0gaXMgcmVzdHJpY3RlZCBpbgpjZXJ0YWluIGNvdW50cmllcyBl
-aXRoZXIgYnkgcGF0ZW50cyBvciBieSBjb3B5cmlnaHRlZCBpbnRlcmZhY2VzLCB0aGUKb3JpZ2lu
-YWwgY29weXJpZ2h0IGhvbGRlciB3aG8gcGxhY2VzIHRoZSBQcm9ncmFtIHVuZGVyIHRoaXMgTGlj
-ZW5zZQptYXkgYWRkIGFuIGV4cGxpY2l0IGdlb2dyYXBoaWNhbCBkaXN0cmlidXRpb24gbGltaXRh
-dGlvbiBleGNsdWRpbmcKdGhvc2UgY291bnRyaWVzLCBzbyB0aGF0IGRpc3RyaWJ1dGlvbiBpcyBw
-ZXJtaXR0ZWQgb25seSBpbiBvciBhbW9uZwpjb3VudHJpZXMgbm90IHRodXMgZXhjbHVkZWQuICBJ
-biBzdWNoIGNhc2UsIHRoaXMgTGljZW5zZSBpbmNvcnBvcmF0ZXMKdGhlIGxpbWl0YXRpb24gYXMg
-aWYgd3JpdHRlbiBpbiB0aGUgYm9keSBvZiB0aGlzIExpY2Vuc2UuCgogIDkuIFRoZSBGcmVlIFNv
-ZnR3YXJlIEZvdW5kYXRpb24gbWF5IHB1Ymxpc2ggcmV2aXNlZCBhbmQvb3IgbmV3IHZlcnNpb25z
-Cm9mIHRoZSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZyb20gdGltZSB0byB0aW1lLiAgU3VjaCBu
-ZXcgdmVyc2lvbnMgd2lsbApiZSBzaW1pbGFyIGluIHNwaXJpdCB0byB0aGUgcHJlc2VudCB2ZXJz
-aW9uLCBidXQgbWF5IGRpZmZlciBpbiBkZXRhaWwgdG8KYWRkcmVzcyBuZXcgcHJvYmxlbXMgb3Ig
-Y29uY2VybnMuCgpFYWNoIHZlcnNpb24gaXMgZ2l2ZW4gYSBkaXN0aW5ndWlzaGluZyB2ZXJzaW9u
-IG51bWJlci4gIElmIHRoZSBQcm9ncmFtCnNwZWNpZmllcyBhIHZlcnNpb24gbnVtYmVyIG9mIHRo
-aXMgTGljZW5zZSB3aGljaCBhcHBsaWVzIHRvIGl0IGFuZCAiYW55CmxhdGVyIHZlcnNpb24iLCB5
-b3UgaGF2ZSB0aGUgb3B0aW9uIG9mIGZvbGxvd2luZyB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnMK
-ZWl0aGVyIG9mIHRoYXQgdmVyc2lvbiBvciBvZiBhbnkgbGF0ZXIgdmVyc2lvbiBwdWJsaXNoZWQg
-YnkgdGhlIEZyZWUKU29mdHdhcmUgRm91bmRhdGlvbi4gIElmIHRoZSBQcm9ncmFtIGRvZXMgbm90
-IHNwZWNpZnkgYSB2ZXJzaW9uIG51bWJlciBvZgp0aGlzIExpY2Vuc2UsIHlvdSBtYXkgY2hvb3Nl
-IGFueSB2ZXJzaW9uIGV2ZXIgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlCkZvdW5kYXRp
-b24uCgogIDEwLiBJZiB5b3Ugd2lzaCB0byBpbmNvcnBvcmF0ZSBwYXJ0cyBvZiB0aGUgUHJvZ3Jh
-bSBpbnRvIG90aGVyIGZyZWUKcHJvZ3JhbXMgd2hvc2UgZGlzdHJpYnV0aW9uIGNvbmRpdGlvbnMg
-YXJlIGRpZmZlcmVudCwgd3JpdGUgdG8gdGhlIGF1dGhvcgp0byBhc2sgZm9yIHBlcm1pc3Npb24u
-ICBGb3Igc29mdHdhcmUgd2hpY2ggaXMgY29weXJpZ2h0ZWQgYnkgdGhlIEZyZWUKU29mdHdhcmUg
-Rm91bmRhdGlvbiwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgd2Ugc29t
-ZXRpbWVzCm1ha2UgZXhjZXB0aW9ucyBmb3IgdGhpcy4gIE91ciBkZWNpc2lvbiB3aWxsIGJlIGd1
-aWRlZCBieSB0aGUgdHdvIGdvYWxzCm9mIHByZXNlcnZpbmcgdGhlIGZyZWUgc3RhdHVzIG9mIGFs
-bCBkZXJpdmF0aXZlcyBvZiBvdXIgZnJlZSBzb2Z0d2FyZSBhbmQKb2YgcHJvbW90aW5nIHRoZSBz
-aGFyaW5nIGFuZCByZXVzZSBvZiBzb2Z0d2FyZSBnZW5lcmFsbHkuCgoJCQkgICAgTk8gV0FSUkFO
-VFkKCiAgMTEuIEJFQ0FVU0UgVEhFIFBST0dSQU0gSVMgTElDRU5TRUQgRlJFRSBPRiBDSEFSR0Us
-IFRIRVJFIElTIE5PIFdBUlJBTlRZCkZPUiBUSEUgUFJPR1JBTSwgVE8gVEhFIEVYVEVOVCBQRVJN
-SVRURUQgQlkgQVBQTElDQUJMRSBMQVcuICBFWENFUFQgV0hFTgpPVEhFUldJU0UgU1RBVEVEIElO
-IFdSSVRJTkcgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFORC9PUiBPVEhFUiBQQVJUSUVTClBST1ZJ
-REUgVEhFIFBST0dSQU0gIkFTIElTIiBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFSVRI
-RVIgRVhQUkVTU0VECk9SIElNUExJRUQsIElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBU
-SEUgSU1QTElFRCBXQVJSQU5USUVTIE9GCk1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1Ig
-QSBQQVJUSUNVTEFSIFBVUlBPU0UuICBUSEUgRU5USVJFIFJJU0sgQVMKVE8gVEhFIFFVQUxJVFkg
-QU5EIFBFUkZPUk1BTkNFIE9GIFRIRSBQUk9HUkFNIElTIFdJVEggWU9VLiAgU0hPVUxEIFRIRQpQ
-Uk9HUkFNIFBST1ZFIERFRkVDVElWRSwgWU9VIEFTU1VNRSBUSEUgQ09TVCBPRiBBTEwgTkVDRVNT
-QVJZIFNFUlZJQ0lORywKUkVQQUlSIE9SIENPUlJFQ1RJT04uCgogIDEyLiBJTiBOTyBFVkVOVCBV
-TkxFU1MgUkVRVUlSRUQgQlkgQVBQTElDQUJMRSBMQVcgT1IgQUdSRUVEIFRPIElOIFdSSVRJTkcK
-V0lMTCBBTlkgQ09QWVJJR0hUIEhPTERFUiwgT1IgQU5ZIE9USEVSIFBBUlRZIFdITyBNQVkgTU9E
-SUZZIEFORC9PUgpSRURJU1RSSUJVVEUgVEhFIFBST0dSQU0gQVMgUEVSTUlUVEVEIEFCT1ZFLCBC
-RSBMSUFCTEUgVE8gWU9VIEZPUiBEQU1BR0VTLApJTkNMVURJTkcgQU5ZIEdFTkVSQUwsIFNQRUNJ
-QUwsIElOQ0lERU5UQUwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIEFSSVNJTkcKT1VUIE9GIFRI
-RSBVU0UgT1IgSU5BQklMSVRZIFRPIFVTRSBUSEUgUFJPR1JBTSAoSU5DTFVESU5HIEJVVCBOT1Qg
-TElNSVRFRApUTyBMT1NTIE9GIERBVEEgT1IgREFUQSBCRUlORyBSRU5ERVJFRCBJTkFDQ1VSQVRF
-IE9SIExPU1NFUyBTVVNUQUlORUQgQlkKWU9VIE9SIFRISVJEIFBBUlRJRVMgT1IgQSBGQUlMVVJF
-IE9GIFRIRSBQUk9HUkFNIFRPIE9QRVJBVEUgV0lUSCBBTlkgT1RIRVIKUFJPR1JBTVMpLCBFVkVO
-IElGIFNVQ0ggSE9MREVSIE9SIE9USEVSIFBBUlRZIEhBUyBCRUVOIEFEVklTRUQgT0YgVEhFClBP
-U1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFUy4KCgkJICAgICBFTkQgT0YgVEVSTVMgQU5EIENPTkRJ
-VElPTlMKDAoJICAgIEhvdyB0byBBcHBseSBUaGVzZSBUZXJtcyB0byBZb3VyIE5ldyBQcm9ncmFt
-cwoKICBJZiB5b3UgZGV2ZWxvcCBhIG5ldyBwcm9ncmFtLCBhbmQgeW91IHdhbnQgaXQgdG8gYmUg
-b2YgdGhlIGdyZWF0ZXN0CnBvc3NpYmxlIHVzZSB0byB0aGUgcHVibGljLCB0aGUgYmVzdCB3YXkg
-dG8gYWNoaWV2ZSB0aGlzIGlzIHRvIG1ha2UgaXQKZnJlZSBzb2Z0d2FyZSB3aGljaCBldmVyeW9u
-ZSBjYW4gcmVkaXN0cmlidXRlIGFuZCBjaGFuZ2UgdW5kZXIgdGhlc2UgdGVybXMuCgogIFRvIGRv
-IHNvLCBhdHRhY2ggdGhlIGZvbGxvd2luZyBub3RpY2VzIHRvIHRoZSBwcm9ncmFtLiAgSXQgaXMg
-c2FmZXN0CnRvIGF0dGFjaCB0aGVtIHRvIHRoZSBzdGFydCBvZiBlYWNoIHNvdXJjZSBmaWxlIHRv
-IG1vc3QgZWZmZWN0aXZlbHkKY29udmV5IHRoZSBleGNsdXNpb24gb2Ygd2FycmFudHk7IGFuZCBl
-YWNoIGZpbGUgc2hvdWxkIGhhdmUgYXQgbGVhc3QKdGhlICJjb3B5cmlnaHQiIGxpbmUgYW5kIGEg
-cG9pbnRlciB0byB3aGVyZSB0aGUgZnVsbCBub3RpY2UgaXMgZm91bmQuCgogICAgPG9uZSBsaW5l
-IHRvIGdpdmUgdGhlIHByb2dyYW0ncyBuYW1lIGFuZCBhIGJyaWVmIGlkZWEgb2Ygd2hhdCBpdCBk
-b2VzLj4KICAgIENvcHlyaWdodCAoQykgPHllYXI+ICA8bmFtZSBvZiBhdXRob3I+CgogICAgVGhp
-cyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9v
-ciBtb2RpZnkKICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGlj
-IExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5CiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9u
-OyBlaXRoZXIgdmVyc2lvbiAyIG9mIHRoZSBMaWNlbnNlLCBvcgogICAgKGF0IHlvdXIgb3B0aW9u
-KSBhbnkgbGF0ZXIgdmVyc2lvbi4KCiAgICBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4g
-dGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJS
-QU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiAgICBNRVJDSEFOVEFC
-SUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiAgICBH
-TlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgoKICAgIFlvdSBzaG91
-bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNl
-CiAgICBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBT
-b2Z0d2FyZQogICAgRm91bmRhdGlvbiwgSW5jLiwgNTkgVGVtcGxlIFBsYWNlLCBTdWl0ZSAzMzAs
-IEJvc3RvbiwgTUEgIDAyMTExLTEzMDcgIFVTQQoKCkFsc28gYWRkIGluZm9ybWF0aW9uIG9uIGhv
-dyB0byBjb250YWN0IHlvdSBieSBlbGVjdHJvbmljIGFuZCBwYXBlciBtYWlsLgoKSWYgdGhlIHBy
-b2dyYW0gaXMgaW50ZXJhY3RpdmUsIG1ha2UgaXQgb3V0cHV0IGEgc2hvcnQgbm90aWNlIGxpa2Ug
-dGhpcwp3aGVuIGl0IHN0YXJ0cyBpbiBhbiBpbnRlcmFjdGl2ZSBtb2RlOgoKICAgIEdub21vdmlz
-aW9uIHZlcnNpb24gNjksIENvcHlyaWdodCAoQykgeWVhciBuYW1lIG9mIGF1dGhvcgogICAgR25v
-bW92aXNpb24gY29tZXMgd2l0aCBBQlNPTFVURUxZIE5PIFdBUlJBTlRZOyBmb3IgZGV0YWlscyB0
-eXBlIGBzaG93IHcnLgogICAgVGhpcyBpcyBmcmVlIHNvZnR3YXJlLCBhbmQgeW91IGFyZSB3ZWxj
-b21lIHRvIHJlZGlzdHJpYnV0ZSBpdAogICAgdW5kZXIgY2VydGFpbiBjb25kaXRpb25zOyB0eXBl
-IGBzaG93IGMnIGZvciBkZXRhaWxzLgoKVGhlIGh5cG90aGV0aWNhbCBjb21tYW5kcyBgc2hvdyB3
-JyBhbmQgYHNob3cgYycgc2hvdWxkIHNob3cgdGhlIGFwcHJvcHJpYXRlCnBhcnRzIG9mIHRoZSBH
-ZW5lcmFsIFB1YmxpYyBMaWNlbnNlLiAgT2YgY291cnNlLCB0aGUgY29tbWFuZHMgeW91IHVzZSBt
-YXkKYmUgY2FsbGVkIHNvbWV0aGluZyBvdGhlciB0aGFuIGBzaG93IHcnIGFuZCBgc2hvdyBjJzsg
-dGhleSBjb3VsZCBldmVuIGJlCm1vdXNlLWNsaWNrcyBvciBtZW51IGl0ZW1zLS13aGF0ZXZlciBz
-dWl0cyB5b3VyIHByb2dyYW0uCgpZb3Ugc2hvdWxkIGFsc28gZ2V0IHlvdXIgZW1wbG95ZXIgKGlm
-IHlvdSB3b3JrIGFzIGEgcHJvZ3JhbW1lcikgb3IgeW91cgpzY2hvb2wsIGlmIGFueSwgdG8gc2ln
-biBhICJjb3B5cmlnaHQgZGlzY2xhaW1lciIgZm9yIHRoZSBwcm9ncmFtLCBpZgpuZWNlc3Nhcnku
-ICBIZXJlIGlzIGEgc2FtcGxlOyBhbHRlciB0aGUgbmFtZXM6CgogIFlveW9keW5lLCBJbmMuLCBo
-ZXJlYnkgZGlzY2xhaW1zIGFsbCBjb3B5cmlnaHQgaW50ZXJlc3QgaW4gdGhlIHByb2dyYW0KICBg
-R25vbW92aXNpb24nICh3aGljaCBtYWtlcyBwYXNzZXMgYXQgY29tcGlsZXJzKSB3cml0dGVuIGJ5
-IEphbWVzIEhhY2tlci4KCiAgPHNpZ25hdHVyZSBvZiBUeSBDb29uPiwgMSBBcHJpbCAxOTg5CiAg
-VHkgQ29vbiwgUHJlc2lkZW50IG9mIFZpY2UKClRoaXMgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBk
-b2VzIG5vdCBwZXJtaXQgaW5jb3Jwb3JhdGluZyB5b3VyIHByb2dyYW0gaW50bwpwcm9wcmlldGFy
-eSBwcm9ncmFtcy4gIElmIHlvdXIgcHJvZ3JhbSBpcyBhIHN1YnJvdXRpbmUgbGlicmFyeSwgeW91
-IG1heQpjb25zaWRlciBpdCBtb3JlIHVzZWZ1bCB0byBwZXJtaXQgbGlua2luZyBwcm9wcmlldGFy
-eSBhcHBsaWNhdGlvbnMgd2l0aCB0aGUKbGlicmFyeS4gIElmIHRoaXMgaXMgd2hhdCB5b3Ugd2Fu
-dCB0byBkbywgdXNlIHRoZSBHTlUgTGlicmFyeSBHZW5lcmFsClB1YmxpYyBMaWNlbnNlIGluc3Rl
-YWQgb2YgdGhpcyBMaWNlbnNlLgo=
-ClxjaGFwdGVye1RoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZX0KClxiZWdpbntjZW50ZXJ9
-CntccGFyaW5kZW50IDBpbgoKVmVyc2lvbiAyLCBKdW5lIDE5OTEKCkNvcHlyaWdodCBcY29weXJp
-Z2h0XCAxOTg5LCAxOTkxIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgSW5jLgoKXGJpZ3NraXAK
-CjU5IFRlbXBsZSBQbGFjZSAtIFN1aXRlIDMzMCwgQm9zdG9uLCBNQSAgMDIxMTEtMTMwNywgVVNB
-CgpcYmlnc2tpcAoKRXZlcnlvbmUgaXMgcGVybWl0dGVkIHRvIGNvcHkgYW5kIGRpc3RyaWJ1dGUg
-dmVyYmF0aW0gY29waWVzCm9mIHRoaXMgbGljZW5zZSBkb2N1bWVudCwgYnV0IGNoYW5naW5nIGl0
-IGlzIG5vdCBhbGxvd2VkLgp9ClxlbmR7Y2VudGVyfQoKXGJlZ2lue2NlbnRlcn0Ke1xiZlxsYXJn
-ZSBQcmVhbWJsZX0KXGVuZHtjZW50ZXJ9CgoKVGhlIGxpY2Vuc2VzIGZvciBtb3N0IHNvZnR3YXJl
-IGFyZSBkZXNpZ25lZCB0byB0YWtlIGF3YXkgeW91ciBmcmVlZG9tIHRvCnNoYXJlIGFuZCBjaGFu
-Z2UgaXQuICBCeSBjb250cmFzdCwgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGlzCmlu
-dGVuZGVkIHRvIGd1YXJhbnRlZSB5b3VyIGZyZWVkb20gdG8gc2hhcmUgYW5kIGNoYW5nZSBmcmVl
-IHNvZnR3YXJlLS0tdG8KbWFrZSBzdXJlIHRoZSBzb2Z0d2FyZSBpcyBmcmVlIGZvciBhbGwgaXRz
-IHVzZXJzLiAgVGhpcyBHZW5lcmFsIFB1YmxpYwpMaWNlbnNlIGFwcGxpZXMgdG8gbW9zdCBvZiB0
-aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uJ3Mgc29mdHdhcmUgYW5kIHRvCmFueSBvdGhlciBw
-cm9ncmFtIHdob3NlIGF1dGhvcnMgY29tbWl0IHRvIHVzaW5nIGl0LiAgKFNvbWUgb3RoZXIgRnJl
-ZQpTb2Z0d2FyZSBGb3VuZGF0aW9uIHNvZnR3YXJlIGlzIGNvdmVyZWQgYnkgdGhlIEdOVSBMaWJy
-YXJ5IEdlbmVyYWwgUHVibGljCkxpY2Vuc2UgaW5zdGVhZC4pICBZb3UgY2FuIGFwcGx5IGl0IHRv
-IHlvdXIgcHJvZ3JhbXMsIHRvby4KCldoZW4gd2Ugc3BlYWsgb2YgZnJlZSBzb2Z0d2FyZSwgd2Ug
-YXJlIHJlZmVycmluZyB0byBmcmVlZG9tLCBub3QgcHJpY2UuCk91ciBHZW5lcmFsIFB1YmxpYyBM
-aWNlbnNlcyBhcmUgZGVzaWduZWQgdG8gbWFrZSBzdXJlIHRoYXQgeW91IGhhdmUgdGhlCmZyZWVk
-b20gdG8gZGlzdHJpYnV0ZSBjb3BpZXMgb2YgZnJlZSBzb2Z0d2FyZSAoYW5kIGNoYXJnZSBmb3Ig
-dGhpcyBzZXJ2aWNlCmlmIHlvdSB3aXNoKSwgdGhhdCB5b3UgcmVjZWl2ZSBzb3VyY2UgY29kZSBv
-ciBjYW4gZ2V0IGl0IGlmIHlvdSB3YW50IGl0LAp0aGF0IHlvdSBjYW4gY2hhbmdlIHRoZSBzb2Z0
-d2FyZSBvciB1c2UgcGllY2VzIG9mIGl0IGluIG5ldyBmcmVlIHByb2dyYW1zOwphbmQgdGhhdCB5
-b3Uga25vdyB5b3UgY2FuIGRvIHRoZXNlIHRoaW5ncy4KClRvIHByb3RlY3QgeW91ciByaWdodHMs
-IHdlIG5lZWQgdG8gbWFrZSByZXN0cmljdGlvbnMgdGhhdCBmb3JiaWQgYW55b25lIHRvCmRlbnkg
-eW91IHRoZXNlIHJpZ2h0cyBvciB0byBhc2sgeW91IHRvIHN1cnJlbmRlciB0aGUgcmlnaHRzLiAg
-VGhlc2UKcmVzdHJpY3Rpb25zIHRyYW5zbGF0ZSB0byBjZXJ0YWluIHJlc3BvbnNpYmlsaXRpZXMg
-Zm9yIHlvdSBpZiB5b3UKZGlzdHJpYnV0ZSBjb3BpZXMgb2YgdGhlIHNvZnR3YXJlLCBvciBpZiB5
-b3UgbW9kaWZ5IGl0LgoKRm9yIGV4YW1wbGUsIGlmIHlvdSBkaXN0cmlidXRlIGNvcGllcyBvZiBz
-dWNoIGEgcHJvZ3JhbSwgd2hldGhlciBncmF0aXMgb3IKZm9yIGEgZmVlLCB5b3UgbXVzdCBnaXZl
-IHRoZSByZWNpcGllbnRzIGFsbCB0aGUgcmlnaHRzIHRoYXQgeW91IGhhdmUuICBZb3UKbXVzdCBt
-YWtlIHN1cmUgdGhhdCB0aGV5LCB0b28sIHJlY2VpdmUgb3IgY2FuIGdldCB0aGUgc291cmNlIGNv
-ZGUuICBBbmQKeW91IG11c3Qgc2hvdyB0aGVtIHRoZXNlIHRlcm1zIHNvIHRoZXkga25vdyB0aGVp
-ciByaWdodHMuCgpXZSBwcm90ZWN0IHlvdXIgcmlnaHRzIHdpdGggdHdvIHN0ZXBzOiAoMSkgY29w
-eXJpZ2h0IHRoZSBzb2Z0d2FyZSwgYW5kICgyKQpvZmZlciB5b3UgdGhpcyBsaWNlbnNlIHdoaWNo
-IGdpdmVzIHlvdSBsZWdhbCBwZXJtaXNzaW9uIHRvIGNvcHksCmRpc3RyaWJ1dGUgYW5kL29yIG1v
-ZGlmeSB0aGUgc29mdHdhcmUuCgpBbHNvLCBmb3IgZWFjaCBhdXRob3IncyBwcm90ZWN0aW9uIGFu
-ZCBvdXJzLCB3ZSB3YW50IHRvIG1ha2UgY2VydGFpbiB0aGF0CmV2ZXJ5b25lIHVuZGVyc3RhbmRz
-IHRoYXQgdGhlcmUgaXMgbm8gd2FycmFudHkgZm9yIHRoaXMgZnJlZSBzb2Z0d2FyZS4gIElmCnRo
-ZSBzb2Z0d2FyZSBpcyBtb2RpZmllZCBieSBzb21lb25lIGVsc2UgYW5kIHBhc3NlZCBvbiwgd2Ug
-d2FudCBpdHMKcmVjaXBpZW50cyB0byBrbm93IHRoYXQgd2hhdCB0aGV5IGhhdmUgaXMgbm90IHRo
-ZSBvcmlnaW5hbCwgc28gdGhhdCBhbnkKcHJvYmxlbXMgaW50cm9kdWNlZCBieSBvdGhlcnMgd2ls
-bCBub3QgcmVmbGVjdCBvbiB0aGUgb3JpZ2luYWwgYXV0aG9ycycKcmVwdXRhdGlvbnMuCgpGaW5h
-bGx5LCBhbnkgZnJlZSBwcm9ncmFtIGlzIHRocmVhdGVuZWQgY29uc3RhbnRseSBieSBzb2Z0d2Fy
-ZSBwYXRlbnRzLgpXZSB3aXNoIHRvIGF2b2lkIHRoZSBkYW5nZXIgdGhhdCByZWRpc3RyaWJ1dG9y
-cyBvZiBhIGZyZWUgcHJvZ3JhbSB3aWxsCmluZGl2aWR1YWxseSBvYnRhaW4gcGF0ZW50IGxpY2Vu
-c2VzLCBpbiBlZmZlY3QgbWFraW5nIHRoZSBwcm9ncmFtCnByb3ByaWV0YXJ5LiAgVG8gcHJldmVu
-dCB0aGlzLCB3ZSBoYXZlIG1hZGUgaXQgY2xlYXIgdGhhdCBhbnkgcGF0ZW50IG11c3QKYmUgbGlj
-ZW5zZWQgZm9yIGV2ZXJ5b25lJ3MgZnJlZSB1c2Ugb3Igbm90IGxpY2Vuc2VkIGF0IGFsbC4KClRo
-ZSBwcmVjaXNlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciBjb3B5aW5nLCBkaXN0cmlidXRpb24g
-YW5kCm1vZGlmaWNhdGlvbiBmb2xsb3cuCgpcYmVnaW57Y2VudGVyfQp7XExhcmdlIFxzYyBUZXJt
-cyBhbmQgQ29uZGl0aW9ucyBGb3IgQ29weWluZywgRGlzdHJpYnV0aW9uIGFuZAogIE1vZGlmaWNh
-dGlvbn0KXGVuZHtjZW50ZXJ9CgoKJVxyZW5ld2NvbW1hbmR7XHRoZWVudW1pfXtcYWxwaGF7ZW51
-bWl9fQpcYmVnaW57ZW51bWVyYXRlfQoKXGFkZHRvY291bnRlcntlbnVtaX17LTF9CgpcaXRlbSAK
-ClRoaXMgTGljZW5zZSBhcHBsaWVzIHRvIGFueSBwcm9ncmFtIG9yIG90aGVyIHdvcmsgd2hpY2gg
-Y29udGFpbnMgYSBub3RpY2UKcGxhY2VkIGJ5IHRoZSBjb3B5cmlnaHQgaG9sZGVyIHNheWluZyBp
-dCBtYXkgYmUgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlCnRlcm1zIG9mIHRoaXMgR2VuZXJhbCBQdWJs
-aWMgTGljZW5zZS4gIFRoZSBgYFByb2dyYW0nJywgYmVsb3csIHJlZmVycyB0bwphbnkgc3VjaCBw
-cm9ncmFtIG9yIHdvcmssIGFuZCBhIGBgd29yayBiYXNlZCBvbiB0aGUgUHJvZ3JhbScnIG1lYW5z
-IGVpdGhlcgp0aGUgUHJvZ3JhbSBvciBhbnkgZGVyaXZhdGl2ZSB3b3JrIHVuZGVyIGNvcHlyaWdo
-dCBsYXc6IHRoYXQgaXMgdG8gc2F5LCBhCndvcmsgY29udGFpbmluZyB0aGUgUHJvZ3JhbSBvciBh
-IHBvcnRpb24gb2YgaXQsIGVpdGhlciB2ZXJiYXRpbSBvciB3aXRoCm1vZGlmaWNhdGlvbnMgYW5k
-L29yIHRyYW5zbGF0ZWQgaW50byBhbm90aGVyIGxhbmd1YWdlLiAgKEhlcmVpbmFmdGVyLAp0cmFu
-c2xhdGlvbiBpcyBpbmNsdWRlZCB3aXRob3V0IGxpbWl0YXRpb24gaW4gdGhlIHRlcm0gYGBtb2Rp
-ZmljYXRpb24nJy4pCkVhY2ggbGljZW5zZWUgaXMgYWRkcmVzc2VkIGFzIGBgeW91JycuCgpBY3Rp
-dml0aWVzIG90aGVyIHRoYW4gY29weWluZywgZGlzdHJpYnV0aW9uIGFuZCBtb2RpZmljYXRpb24g
-YXJlIG5vdApjb3ZlcmVkIGJ5IHRoaXMgTGljZW5zZTsgdGhleSBhcmUgb3V0c2lkZSBpdHMgc2Nv
-cGUuICBUaGUgYWN0IG9mCnJ1bm5pbmcgdGhlIFByb2dyYW0gaXMgbm90IHJlc3RyaWN0ZWQsIGFu
-ZCB0aGUgb3V0cHV0IGZyb20gdGhlIFByb2dyYW0KaXMgY292ZXJlZCBvbmx5IGlmIGl0cyBjb250
-ZW50cyBjb25zdGl0dXRlIGEgd29yayBiYXNlZCBvbiB0aGUKUHJvZ3JhbSAoaW5kZXBlbmRlbnQg
-b2YgaGF2aW5nIGJlZW4gbWFkZSBieSBydW5uaW5nIHRoZSBQcm9ncmFtKS4KV2hldGhlciB0aGF0
-IGlzIHRydWUgZGVwZW5kcyBvbiB3aGF0IHRoZSBQcm9ncmFtIGRvZXMuCgpcaXRlbSBZb3UgbWF5
-IGNvcHkgYW5kIGRpc3RyaWJ1dGUgdmVyYmF0aW0gY29waWVzIG9mIHRoZSBQcm9ncmFtJ3Mgc291
-cmNlCiAgY29kZSBhcyB5b3UgcmVjZWl2ZSBpdCwgaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhh
-dCB5b3UgY29uc3BpY3VvdXNseQogIGFuZCBhcHByb3ByaWF0ZWx5IHB1Ymxpc2ggb24gZWFjaCBj
-b3B5IGFuIGFwcHJvcHJpYXRlIGNvcHlyaWdodCBub3RpY2UKICBhbmQgZGlzY2xhaW1lciBvZiB3
-YXJyYW50eTsga2VlcCBpbnRhY3QgYWxsIHRoZSBub3RpY2VzIHRoYXQgcmVmZXIgdG8KICB0aGlz
-IExpY2Vuc2UgYW5kIHRvIHRoZSBhYnNlbmNlIG9mIGFueSB3YXJyYW50eTsgYW5kIGdpdmUgYW55
-IG90aGVyCiAgcmVjaXBpZW50cyBvZiB0aGUgUHJvZ3JhbSBhIGNvcHkgb2YgdGhpcyBMaWNlbnNl
-IGFsb25nIHdpdGggdGhlIFByb2dyYW0uCgpZb3UgbWF5IGNoYXJnZSBhIGZlZSBmb3IgdGhlIHBo
-eXNpY2FsIGFjdCBvZiB0cmFuc2ZlcnJpbmcgYSBjb3B5LCBhbmQgeW91Cm1heSBhdCB5b3VyIG9w
-dGlvbiBvZmZlciB3YXJyYW50eSBwcm90ZWN0aW9uIGluIGV4Y2hhbmdlIGZvciBhIGZlZS4KClxp
-dGVtCgpZb3UgbWF5IG1vZGlmeSB5b3VyIGNvcHkgb3IgY29waWVzIG9mIHRoZSBQcm9ncmFtIG9y
-IGFueSBwb3J0aW9uCm9mIGl0LCB0aHVzIGZvcm1pbmcgYSB3b3JrIGJhc2VkIG9uIHRoZSBQcm9n
-cmFtLCBhbmQgY29weSBhbmQKZGlzdHJpYnV0ZSBzdWNoIG1vZGlmaWNhdGlvbnMgb3Igd29yayB1
-bmRlciB0aGUgdGVybXMgb2YgU2VjdGlvbiAxCmFib3ZlLCBwcm92aWRlZCB0aGF0IHlvdSBhbHNv
-IG1lZXQgYWxsIG9mIHRoZXNlIGNvbmRpdGlvbnM6CgpcYmVnaW57ZW51bWVyYXRlfQoKXGl0ZW0g
-CgpZb3UgbXVzdCBjYXVzZSB0aGUgbW9kaWZpZWQgZmlsZXMgdG8gY2FycnkgcHJvbWluZW50IG5v
-dGljZXMgc3RhdGluZyB0aGF0CnlvdSBjaGFuZ2VkIHRoZSBmaWxlcyBhbmQgdGhlIGRhdGUgb2Yg
-YW55IGNoYW5nZS4KClxpdGVtCgpZb3UgbXVzdCBjYXVzZSBhbnkgd29yayB0aGF0IHlvdSBkaXN0
-cmlidXRlIG9yIHB1Ymxpc2gsIHRoYXQgaW4Kd2hvbGUgb3IgaW4gcGFydCBjb250YWlucyBvciBp
-cyBkZXJpdmVkIGZyb20gdGhlIFByb2dyYW0gb3IgYW55CnBhcnQgdGhlcmVvZiwgdG8gYmUgbGlj
-ZW5zZWQgYXMgYSB3aG9sZSBhdCBubyBjaGFyZ2UgdG8gYWxsIHRoaXJkCnBhcnRpZXMgdW5kZXIg
-dGhlIHRlcm1zIG9mIHRoaXMgTGljZW5zZS4KClxpdGVtCklmIHRoZSBtb2RpZmllZCBwcm9ncmFt
-IG5vcm1hbGx5IHJlYWRzIGNvbW1hbmRzIGludGVyYWN0aXZlbHkKd2hlbiBydW4sIHlvdSBtdXN0
-IGNhdXNlIGl0LCB3aGVuIHN0YXJ0ZWQgcnVubmluZyBmb3Igc3VjaAppbnRlcmFjdGl2ZSB1c2Ug
-aW4gdGhlIG1vc3Qgb3JkaW5hcnkgd2F5LCB0byBwcmludCBvciBkaXNwbGF5IGFuCmFubm91bmNl
-bWVudCBpbmNsdWRpbmcgYW4gYXBwcm9wcmlhdGUgY29weXJpZ2h0IG5vdGljZSBhbmQgYQpub3Rp
-Y2UgdGhhdCB0aGVyZSBpcyBubyB3YXJyYW50eSAob3IgZWxzZSwgc2F5aW5nIHRoYXQgeW91IHBy
-b3ZpZGUKYSB3YXJyYW50eSkgYW5kIHRoYXQgdXNlcnMgbWF5IHJlZGlzdHJpYnV0ZSB0aGUgcHJv
-Z3JhbSB1bmRlcgp0aGVzZSBjb25kaXRpb25zLCBhbmQgdGVsbGluZyB0aGUgdXNlciBob3cgdG8g
-dmlldyBhIGNvcHkgb2YgdGhpcwpMaWNlbnNlLiAgKEV4Y2VwdGlvbjogaWYgdGhlIFByb2dyYW0g
-aXRzZWxmIGlzIGludGVyYWN0aXZlIGJ1dApkb2VzIG5vdCBub3JtYWxseSBwcmludCBzdWNoIGFu
-IGFubm91bmNlbWVudCwgeW91ciB3b3JrIGJhc2VkIG9uCnRoZSBQcm9ncmFtIGlzIG5vdCByZXF1
-aXJlZCB0byBwcmludCBhbiBhbm5vdW5jZW1lbnQuKQoKXGVuZHtlbnVtZXJhdGV9CgoKVGhlc2Ug
-cmVxdWlyZW1lbnRzIGFwcGx5IHRvIHRoZSBtb2RpZmllZCB3b3JrIGFzIGEgd2hvbGUuICBJZgpp
-ZGVudGlmaWFibGUgc2VjdGlvbnMgb2YgdGhhdCB3b3JrIGFyZSBub3QgZGVyaXZlZCBmcm9tIHRo
-ZSBQcm9ncmFtLAphbmQgY2FuIGJlIHJlYXNvbmFibHkgY29uc2lkZXJlZCBpbmRlcGVuZGVudCBh
-bmQgc2VwYXJhdGUgd29ya3MgaW4KdGhlbXNlbHZlcywgdGhlbiB0aGlzIExpY2Vuc2UsIGFuZCBp
-dHMgdGVybXMsIGRvIG5vdCBhcHBseSB0byB0aG9zZQpzZWN0aW9ucyB3aGVuIHlvdSBkaXN0cmli
-dXRlIHRoZW0gYXMgc2VwYXJhdGUgd29ya3MuICBCdXQgd2hlbiB5b3UKZGlzdHJpYnV0ZSB0aGUg
-c2FtZSBzZWN0aW9ucyBhcyBwYXJ0IG9mIGEgd2hvbGUgd2hpY2ggaXMgYSB3b3JrIGJhc2VkCm9u
-IHRoZSBQcm9ncmFtLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB3aG9sZSBtdXN0IGJlIG9uIHRo
-ZSB0ZXJtcyBvZgp0aGlzIExpY2Vuc2UsIHdob3NlIHBlcm1pc3Npb25zIGZvciBvdGhlciBsaWNl
-bnNlZXMgZXh0ZW5kIHRvIHRoZQplbnRpcmUgd2hvbGUsIGFuZCB0aHVzIHRvIGVhY2ggYW5kIGV2
-ZXJ5IHBhcnQgcmVnYXJkbGVzcyBvZiB3aG8gd3JvdGUgaXQuCgpUaHVzLCBpdCBpcyBub3QgdGhl
-IGludGVudCBvZiB0aGlzIHNlY3Rpb24gdG8gY2xhaW0gcmlnaHRzIG9yIGNvbnRlc3QKeW91ciBy
-aWdodHMgdG8gd29yayB3cml0dGVuIGVudGlyZWx5IGJ5IHlvdTsgcmF0aGVyLCB0aGUgaW50ZW50
-IGlzIHRvCmV4ZXJjaXNlIHRoZSByaWdodCB0byBjb250cm9sIHRoZSBkaXN0cmlidXRpb24gb2Yg
-ZGVyaXZhdGl2ZSBvcgpjb2xsZWN0aXZlIHdvcmtzIGJhc2VkIG9uIHRoZSBQcm9ncmFtLgoKSW4g
-YWRkaXRpb24sIG1lcmUgYWdncmVnYXRpb24gb2YgYW5vdGhlciB3b3JrIG5vdCBiYXNlZCBvbiB0
-aGUgUHJvZ3JhbQp3aXRoIHRoZSBQcm9ncmFtIChvciB3aXRoIGEgd29yayBiYXNlZCBvbiB0aGUg
-UHJvZ3JhbSkgb24gYSB2b2x1bWUgb2YKYSBzdG9yYWdlIG9yIGRpc3RyaWJ1dGlvbiBtZWRpdW0g
-ZG9lcyBub3QgYnJpbmcgdGhlIG90aGVyIHdvcmsgdW5kZXIKdGhlIHNjb3BlIG9mIHRoaXMgTGlj
-ZW5zZS4KClxpdGVtCllvdSBtYXkgY29weSBhbmQgZGlzdHJpYnV0ZSB0aGUgUHJvZ3JhbSAob3Ig
-YSB3b3JrIGJhc2VkIG9uIGl0LAp1bmRlciBTZWN0aW9uIDIpIGluIG9iamVjdCBjb2RlIG9yIGV4
-ZWN1dGFibGUgZm9ybSB1bmRlciB0aGUgdGVybXMgb2YKU2VjdGlvbnMgMSBhbmQgMiBhYm92ZSBw
-cm92aWRlZCB0aGF0IHlvdSBhbHNvIGRvIG9uZSBvZiB0aGUgZm9sbG93aW5nOgoKXGJlZ2lue2Vu
-dW1lcmF0ZX0KClxpdGVtCgpBY2NvbXBhbnkgaXQgd2l0aCB0aGUgY29tcGxldGUgY29ycmVzcG9u
-ZGluZyBtYWNoaW5lLXJlYWRhYmxlCnNvdXJjZSBjb2RlLCB3aGljaCBtdXN0IGJlIGRpc3RyaWJ1
-dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiBTZWN0aW9ucwoxIGFuZCAyIGFib3ZlIG9uIGEgbWVkaXVt
-IGN1c3RvbWFyaWx5IHVzZWQgZm9yIHNvZnR3YXJlIGludGVyY2hhbmdlOyBvciwKClxpdGVtCgpB
-Y2NvbXBhbnkgaXQgd2l0aCBhIHdyaXR0ZW4gb2ZmZXIsIHZhbGlkIGZvciBhdCBsZWFzdCB0aHJl
-ZQp5ZWFycywgdG8gZ2l2ZSBhbnkgdGhpcmQgcGFydHksIGZvciBhIGNoYXJnZSBubyBtb3JlIHRo
-YW4geW91cgpjb3N0IG9mIHBoeXNpY2FsbHkgcGVyZm9ybWluZyBzb3VyY2UgZGlzdHJpYnV0aW9u
-LCBhIGNvbXBsZXRlCm1hY2hpbmUtcmVhZGFibGUgY29weSBvZiB0aGUgY29ycmVzcG9uZGluZyBz
-b3VyY2UgY29kZSwgdG8gYmUKZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIFNlY3Rpb25z
-IDEgYW5kIDIgYWJvdmUgb24gYSBtZWRpdW0KY3VzdG9tYXJpbHkgdXNlZCBmb3Igc29mdHdhcmUg
-aW50ZXJjaGFuZ2U7IG9yLAoKXGl0ZW0KCkFjY29tcGFueSBpdCB3aXRoIHRoZSBpbmZvcm1hdGlv
-biB5b3UgcmVjZWl2ZWQgYXMgdG8gdGhlIG9mZmVyCnRvIGRpc3RyaWJ1dGUgY29ycmVzcG9uZGlu
-ZyBzb3VyY2UgY29kZS4gIChUaGlzIGFsdGVybmF0aXZlIGlzCmFsbG93ZWQgb25seSBmb3Igbm9u
-Y29tbWVyY2lhbCBkaXN0cmlidXRpb24gYW5kIG9ubHkgaWYgeW91CnJlY2VpdmVkIHRoZSBwcm9n
-cmFtIGluIG9iamVjdCBjb2RlIG9yIGV4ZWN1dGFibGUgZm9ybSB3aXRoIHN1Y2gKYW4gb2ZmZXIs
-IGluIGFjY29yZCB3aXRoIFN1YnNlY3Rpb24gYiBhYm92ZS4pCgpcZW5ke2VudW1lcmF0ZX0KCgpU
-aGUgc291cmNlIGNvZGUgZm9yIGEgd29yayBtZWFucyB0aGUgcHJlZmVycmVkIGZvcm0gb2YgdGhl
-IHdvcmsgZm9yCm1ha2luZyBtb2RpZmljYXRpb25zIHRvIGl0LiAgRm9yIGFuIGV4ZWN1dGFibGUg
-d29yaywgY29tcGxldGUgc291cmNlCmNvZGUgbWVhbnMgYWxsIHRoZSBzb3VyY2UgY29kZSBmb3Ig
-YWxsIG1vZHVsZXMgaXQgY29udGFpbnMsIHBsdXMgYW55CmFzc29jaWF0ZWQgaW50ZXJmYWNlIGRl
-ZmluaXRpb24gZmlsZXMsIHBsdXMgdGhlIHNjcmlwdHMgdXNlZCB0bwpjb250cm9sIGNvbXBpbGF0
-aW9uIGFuZCBpbnN0YWxsYXRpb24gb2YgdGhlIGV4ZWN1dGFibGUuICBIb3dldmVyLCBhcyBhCnNw
-ZWNpYWwgZXhjZXB0aW9uLCB0aGUgc291cmNlIGNvZGUgZGlzdHJpYnV0ZWQgbmVlZCBub3QgaW5j
-bHVkZQphbnl0aGluZyB0aGF0IGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIChpbiBlaXRoZXIgc291
-cmNlIG9yIGJpbmFyeQpmb3JtKSB3aXRoIHRoZSBtYWpvciBjb21wb25lbnRzIChjb21waWxlciwg
-a2VybmVsLCBhbmQgc28gb24pIG9mIHRoZQpvcGVyYXRpbmcgc3lzdGVtIG9uIHdoaWNoIHRoZSBl
-eGVjdXRhYmxlIHJ1bnMsIHVubGVzcyB0aGF0IGNvbXBvbmVudAppdHNlbGYgYWNjb21wYW5pZXMg
-dGhlIGV4ZWN1dGFibGUuCgpJZiBkaXN0cmlidXRpb24gb2YgZXhlY3V0YWJsZSBvciBvYmplY3Qg
-Y29kZSBpcyBtYWRlIGJ5IG9mZmVyaW5nCmFjY2VzcyB0byBjb3B5IGZyb20gYSBkZXNpZ25hdGVk
-IHBsYWNlLCB0aGVuIG9mZmVyaW5nIGVxdWl2YWxlbnQKYWNjZXNzIHRvIGNvcHkgdGhlIHNvdXJj
-ZSBjb2RlIGZyb20gdGhlIHNhbWUgcGxhY2UgY291bnRzIGFzCmRpc3RyaWJ1dGlvbiBvZiB0aGUg
-c291cmNlIGNvZGUsIGV2ZW4gdGhvdWdoIHRoaXJkIHBhcnRpZXMgYXJlIG5vdApjb21wZWxsZWQg
-dG8gY29weSB0aGUgc291cmNlIGFsb25nIHdpdGggdGhlIG9iamVjdCBjb2RlLgoKXGl0ZW0KWW91
-IG1heSBub3QgY29weSwgbW9kaWZ5LCBzdWJsaWNlbnNlLCBvciBkaXN0cmlidXRlIHRoZSBQcm9n
-cmFtCmV4Y2VwdCBhcyBleHByZXNzbHkgcHJvdmlkZWQgdW5kZXIgdGhpcyBMaWNlbnNlLiAgQW55
-IGF0dGVtcHQKb3RoZXJ3aXNlIHRvIGNvcHksIG1vZGlmeSwgc3VibGljZW5zZSBvciBkaXN0cmli
-dXRlIHRoZSBQcm9ncmFtIGlzCnZvaWQsIGFuZCB3aWxsIGF1dG9tYXRpY2FsbHkgdGVybWluYXRl
-IHlvdXIgcmlnaHRzIHVuZGVyIHRoaXMgTGljZW5zZS4KSG93ZXZlciwgcGFydGllcyB3aG8gaGF2
-ZSByZWNlaXZlZCBjb3BpZXMsIG9yIHJpZ2h0cywgZnJvbSB5b3UgdW5kZXIKdGhpcyBMaWNlbnNl
-IHdpbGwgbm90IGhhdmUgdGhlaXIgbGljZW5zZXMgdGVybWluYXRlZCBzbyBsb25nIGFzIHN1Y2gK
-cGFydGllcyByZW1haW4gaW4gZnVsbCBjb21wbGlhbmNlLgoKXGl0ZW0KWW91IGFyZSBub3QgcmVx
-dWlyZWQgdG8gYWNjZXB0IHRoaXMgTGljZW5zZSwgc2luY2UgeW91IGhhdmUgbm90CnNpZ25lZCBp
-dC4gIEhvd2V2ZXIsIG5vdGhpbmcgZWxzZSBncmFudHMgeW91IHBlcm1pc3Npb24gdG8gbW9kaWZ5
-IG9yCmRpc3RyaWJ1dGUgdGhlIFByb2dyYW0gb3IgaXRzIGRlcml2YXRpdmUgd29ya3MuICBUaGVz
-ZSBhY3Rpb25zIGFyZQpwcm9oaWJpdGVkIGJ5IGxhdyBpZiB5b3UgZG8gbm90IGFjY2VwdCB0aGlz
-IExpY2Vuc2UuICBUaGVyZWZvcmUsIGJ5Cm1vZGlmeWluZyBvciBkaXN0cmlidXRpbmcgdGhlIFBy
-b2dyYW0gKG9yIGFueSB3b3JrIGJhc2VkIG9uIHRoZQpQcm9ncmFtKSwgeW91IGluZGljYXRlIHlv
-dXIgYWNjZXB0YW5jZSBvZiB0aGlzIExpY2Vuc2UgdG8gZG8gc28sIGFuZAphbGwgaXRzIHRlcm1z
-IGFuZCBjb25kaXRpb25zIGZvciBjb3B5aW5nLCBkaXN0cmlidXRpbmcgb3IgbW9kaWZ5aW5nCnRo
-ZSBQcm9ncmFtIG9yIHdvcmtzIGJhc2VkIG9uIGl0LgoKXGl0ZW0KRWFjaCB0aW1lIHlvdSByZWRp
-c3RyaWJ1dGUgdGhlIFByb2dyYW0gKG9yIGFueSB3b3JrIGJhc2VkIG9uIHRoZQpQcm9ncmFtKSwg
-dGhlIHJlY2lwaWVudCBhdXRvbWF0aWNhbGx5IHJlY2VpdmVzIGEgbGljZW5zZSBmcm9tIHRoZQpv
-cmlnaW5hbCBsaWNlbnNvciB0byBjb3B5LCBkaXN0cmlidXRlIG9yIG1vZGlmeSB0aGUgUHJvZ3Jh
-bSBzdWJqZWN0IHRvCnRoZXNlIHRlcm1zIGFuZCBjb25kaXRpb25zLiAgWW91IG1heSBub3QgaW1w
-b3NlIGFueSBmdXJ0aGVyCnJlc3RyaWN0aW9ucyBvbiB0aGUgcmVjaXBpZW50cycgZXhlcmNpc2Ug
-b2YgdGhlIHJpZ2h0cyBncmFudGVkIGhlcmVpbi4KWW91IGFyZSBub3QgcmVzcG9uc2libGUgZm9y
-IGVuZm9yY2luZyBjb21wbGlhbmNlIGJ5IHRoaXJkIHBhcnRpZXMgdG8KdGhpcyBMaWNlbnNlLgoK
-XGl0ZW0KSWYsIGFzIGEgY29uc2VxdWVuY2Ugb2YgYSBjb3VydCBqdWRnbWVudCBvciBhbGxlZ2F0
-aW9uIG9mIHBhdGVudAppbmZyaW5nZW1lbnQgb3IgZm9yIGFueSBvdGhlciByZWFzb24gKG5vdCBs
-aW1pdGVkIHRvIHBhdGVudCBpc3N1ZXMpLApjb25kaXRpb25zIGFyZSBpbXBvc2VkIG9uIHlvdSAo
-d2hldGhlciBieSBjb3VydCBvcmRlciwgYWdyZWVtZW50IG9yCm90aGVyd2lzZSkgdGhhdCBjb250
-cmFkaWN0IHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZSwgdGhleSBkbyBub3QKZXhjdXNl
-IHlvdSBmcm9tIHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZS4gIElmIHlvdSBjYW5ub3QK
-ZGlzdHJpYnV0ZSBzbyBhcyB0byBzYXRpc2Z5IHNpbXVsdGFuZW91c2x5IHlvdXIgb2JsaWdhdGlv
-bnMgdW5kZXIgdGhpcwpMaWNlbnNlIGFuZCBhbnkgb3RoZXIgcGVydGluZW50IG9ibGlnYXRpb25z
-LCB0aGVuIGFzIGEgY29uc2VxdWVuY2UgeW91Cm1heSBub3QgZGlzdHJpYnV0ZSB0aGUgUHJvZ3Jh
-bSBhdCBhbGwuICBGb3IgZXhhbXBsZSwgaWYgYSBwYXRlbnQKbGljZW5zZSB3b3VsZCBub3QgcGVy
-bWl0IHJveWFsdHktZnJlZSByZWRpc3RyaWJ1dGlvbiBvZiB0aGUgUHJvZ3JhbSBieQphbGwgdGhv
-c2Ugd2hvIHJlY2VpdmUgY29waWVzIGRpcmVjdGx5IG9yIGluZGlyZWN0bHkgdGhyb3VnaCB5b3Us
-IHRoZW4KdGhlIG9ubHkgd2F5IHlvdSBjb3VsZCBzYXRpc2Z5IGJvdGggaXQgYW5kIHRoaXMgTGlj
-ZW5zZSB3b3VsZCBiZSB0bwpyZWZyYWluIGVudGlyZWx5IGZyb20gZGlzdHJpYnV0aW9uIG9mIHRo
-ZSBQcm9ncmFtLgoKSWYgYW55IHBvcnRpb24gb2YgdGhpcyBzZWN0aW9uIGlzIGhlbGQgaW52YWxp
-ZCBvciB1bmVuZm9yY2VhYmxlIHVuZGVyCmFueSBwYXJ0aWN1bGFyIGNpcmN1bXN0YW5jZSwgdGhl
-IGJhbGFuY2Ugb2YgdGhlIHNlY3Rpb24gaXMgaW50ZW5kZWQgdG8KYXBwbHkgYW5kIHRoZSBzZWN0
-aW9uIGFzIGEgd2hvbGUgaXMgaW50ZW5kZWQgdG8gYXBwbHkgaW4gb3RoZXIKY2lyY3Vtc3RhbmNl
-cy4KCkl0IGlzIG5vdCB0aGUgcHVycG9zZSBvZiB0aGlzIHNlY3Rpb24gdG8gaW5kdWNlIHlvdSB0
-byBpbmZyaW5nZSBhbnkKcGF0ZW50cyBvciBvdGhlciBwcm9wZXJ0eSByaWdodCBjbGFpbXMgb3Ig
-dG8gY29udGVzdCB2YWxpZGl0eSBvZiBhbnkKc3VjaCBjbGFpbXM7IHRoaXMgc2VjdGlvbiBoYXMg
-dGhlIHNvbGUgcHVycG9zZSBvZiBwcm90ZWN0aW5nIHRoZQppbnRlZ3JpdHkgb2YgdGhlIGZyZWUg
-c29mdHdhcmUgZGlzdHJpYnV0aW9uIHN5c3RlbSwgd2hpY2ggaXMKaW1wbGVtZW50ZWQgYnkgcHVi
-bGljIGxpY2Vuc2UgcHJhY3RpY2VzLiAgTWFueSBwZW9wbGUgaGF2ZSBtYWRlCmdlbmVyb3VzIGNv
-bnRyaWJ1dGlvbnMgdG8gdGhlIHdpZGUgcmFuZ2Ugb2Ygc29mdHdhcmUgZGlzdHJpYnV0ZWQKdGhy
-b3VnaCB0aGF0IHN5c3RlbSBpbiByZWxpYW5jZSBvbiBjb25zaXN0ZW50IGFwcGxpY2F0aW9uIG9m
-IHRoYXQKc3lzdGVtOyBpdCBpcyB1cCB0byB0aGUgYXV0aG9yL2Rvbm9yIHRvIGRlY2lkZSBpZiBo
-ZSBvciBzaGUgaXMgd2lsbGluZwp0byBkaXN0cmlidXRlIHNvZnR3YXJlIHRocm91Z2ggYW55IG90
-aGVyIHN5c3RlbSBhbmQgYSBsaWNlbnNlZSBjYW5ub3QKaW1wb3NlIHRoYXQgY2hvaWNlLgoKVGhp
-cyBzZWN0aW9uIGlzIGludGVuZGVkIHRvIG1ha2UgdGhvcm91Z2hseSBjbGVhciB3aGF0IGlzIGJl
-bGlldmVkIHRvCmJlIGEgY29uc2VxdWVuY2Ugb2YgdGhlIHJlc3Qgb2YgdGhpcyBMaWNlbnNlLgoK
-XGl0ZW0KSWYgdGhlIGRpc3RyaWJ1dGlvbiBhbmQvb3IgdXNlIG9mIHRoZSBQcm9ncmFtIGlzIHJl
-c3RyaWN0ZWQgaW4KY2VydGFpbiBjb3VudHJpZXMgZWl0aGVyIGJ5IHBhdGVudHMgb3IgYnkgY29w
-eXJpZ2h0ZWQgaW50ZXJmYWNlcywgdGhlCm9yaWdpbmFsIGNvcHlyaWdodCBob2xkZXIgd2hvIHBs
-YWNlcyB0aGUgUHJvZ3JhbSB1bmRlciB0aGlzIExpY2Vuc2UKbWF5IGFkZCBhbiBleHBsaWNpdCBn
-ZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9uIGxpbWl0YXRpb24gZXhjbHVkaW5nCnRob3NlIGNvdW50
-cmllcywgc28gdGhhdCBkaXN0cmlidXRpb24gaXMgcGVybWl0dGVkIG9ubHkgaW4gb3IgYW1vbmcK
-Y291bnRyaWVzIG5vdCB0aHVzIGV4Y2x1ZGVkLiAgSW4gc3VjaCBjYXNlLCB0aGlzIExpY2Vuc2Ug
-aW5jb3Jwb3JhdGVzCnRoZSBsaW1pdGF0aW9uIGFzIGlmIHdyaXR0ZW4gaW4gdGhlIGJvZHkgb2Yg
-dGhpcyBMaWNlbnNlLgoKXGl0ZW0KVGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiBtYXkgcHVi
-bGlzaCByZXZpc2VkIGFuZC9vciBuZXcgdmVyc2lvbnMKb2YgdGhlIEdlbmVyYWwgUHVibGljIExp
-Y2Vuc2UgZnJvbSB0aW1lIHRvIHRpbWUuICBTdWNoIG5ldyB2ZXJzaW9ucyB3aWxsCmJlIHNpbWls
-YXIgaW4gc3Bpcml0IHRvIHRoZSBwcmVzZW50IHZlcnNpb24sIGJ1dCBtYXkgZGlmZmVyIGluIGRl
-dGFpbCB0bwphZGRyZXNzIG5ldyBwcm9ibGVtcyBvciBjb25jZXJucy4KCkVhY2ggdmVyc2lvbiBp
-cyBnaXZlbiBhIGRpc3Rpbmd1aXNoaW5nIHZlcnNpb24gbnVtYmVyLiAgSWYgdGhlIFByb2dyYW0K
-c3BlY2lmaWVzIGEgdmVyc2lvbiBudW1iZXIgb2YgdGhpcyBMaWNlbnNlIHdoaWNoIGFwcGxpZXMg
-dG8gaXQgYW5kIGBgYW55CmxhdGVyIHZlcnNpb24nJywgeW91IGhhdmUgdGhlIG9wdGlvbiBvZiBm
-b2xsb3dpbmcgdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zCmVpdGhlciBvZiB0aGF0IHZlcnNpb24g
-b3Igb2YgYW55IGxhdGVyIHZlcnNpb24gcHVibGlzaGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZv
-dW5kYXRpb24uICBJZiB0aGUgUHJvZ3JhbSBkb2VzIG5vdCBzcGVjaWZ5IGEgdmVyc2lvbiBudW1i
-ZXIgb2YKdGhpcyBMaWNlbnNlLCB5b3UgbWF5IGNob29zZSBhbnkgdmVyc2lvbiBldmVyIHB1Ymxp
-c2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZQpGb3VuZGF0aW9uLgoKXGl0ZW0KSWYgeW91IHdpc2gg
-dG8gaW5jb3Jwb3JhdGUgcGFydHMgb2YgdGhlIFByb2dyYW0gaW50byBvdGhlciBmcmVlCnByb2dy
-YW1zIHdob3NlIGRpc3RyaWJ1dGlvbiBjb25kaXRpb25zIGFyZSBkaWZmZXJlbnQsIHdyaXRlIHRv
-IHRoZSBhdXRob3IKdG8gYXNrIGZvciBwZXJtaXNzaW9uLiAgRm9yIHNvZnR3YXJlIHdoaWNoIGlz
-IGNvcHlyaWdodGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZvdW5kYXRpb24sIHdyaXRlIHRvIHRo
-ZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHdlIHNvbWV0aW1lcwptYWtlIGV4Y2VwdGlvbnMg
-Zm9yIHRoaXMuICBPdXIgZGVjaXNpb24gd2lsbCBiZSBndWlkZWQgYnkgdGhlIHR3byBnb2Fscwpv
-ZiBwcmVzZXJ2aW5nIHRoZSBmcmVlIHN0YXR1cyBvZiBhbGwgZGVyaXZhdGl2ZXMgb2Ygb3VyIGZy
-ZWUgc29mdHdhcmUgYW5kCm9mIHByb21vdGluZyB0aGUgc2hhcmluZyBhbmQgcmV1c2Ugb2Ygc29m
-dHdhcmUgZ2VuZXJhbGx5LgoKXGJlZ2lue2NlbnRlcn0Ke1xMYXJnZVxzYwpObyBXYXJyYW50eQp9
-ClxlbmR7Y2VudGVyfQoKXGl0ZW0Ke1xzYyBCZWNhdXNlIHRoZSBwcm9ncmFtIGlzIGxpY2Vuc2Vk
-IGZyZWUgb2YgY2hhcmdlLCB0aGVyZSBpcyBubyB3YXJyYW50eQpmb3IgdGhlIHByb2dyYW0sIHRv
-IHRoZSBleHRlbnQgcGVybWl0dGVkIGJ5IGFwcGxpY2FibGUgbGF3LiAgRXhjZXB0IHdoZW4Kb3Ro
-ZXJ3aXNlIHN0YXRlZCBpbiB3cml0aW5nIHRoZSBjb3B5cmlnaHQgaG9sZGVycyBhbmQvb3Igb3Ro
-ZXIgcGFydGllcwpwcm92aWRlIHRoZSBwcm9ncmFtIGBgYXMgaXMnJyB3aXRob3V0IHdhcnJhbnR5
-IG9mIGFueSBraW5kLCBlaXRoZXIgZXhwcmVzc2VkCm9yIGltcGxpZWQsIGluY2x1ZGluZywgYnV0
-IG5vdCBsaW1pdGVkIHRvLCB0aGUgaW1wbGllZCB3YXJyYW50aWVzIG9mCm1lcmNoYW50YWJpbGl0
-eSBhbmQgZml0bmVzcyBmb3IgYSBwYXJ0aWN1bGFyIHB1cnBvc2UuICBUaGUgZW50aXJlIHJpc2sg
-YXMKdG8gdGhlIHF1YWxpdHkgYW5kIHBlcmZvcm1hbmNlIG9mIHRoZSBwcm9ncmFtIGlzIHdpdGgg
-eW91LiAgU2hvdWxkIHRoZQpwcm9ncmFtIHByb3ZlIGRlZmVjdGl2ZSwgeW91IGFzc3VtZSB0aGUg
-Y29zdCBvZiBhbGwgbmVjZXNzYXJ5IHNlcnZpY2luZywKcmVwYWlyIG9yIGNvcnJlY3Rpb24ufQoK
-XGl0ZW0Ke1xzYyBJbiBubyBldmVudCB1bmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcg
-b3IgYWdyZWVkIHRvIGluIHdyaXRpbmcKd2lsbCBhbnkgY29weXJpZ2h0IGhvbGRlciwgb3IgYW55
-IG90aGVyIHBhcnR5IHdobyBtYXkgbW9kaWZ5IGFuZC9vcgpyZWRpc3RyaWJ1dGUgdGhlIHByb2dy
-YW0gYXMgcGVybWl0dGVkIGFib3ZlLCBiZSBsaWFibGUgdG8geW91IGZvciBkYW1hZ2VzLAppbmNs
-dWRpbmcgYW55IGdlbmVyYWwsIHNwZWNpYWwsIGluY2lkZW50YWwgb3IgY29uc2VxdWVudGlhbCBk
-YW1hZ2VzIGFyaXNpbmcKb3V0IG9mIHRoZSB1c2Ugb3IgaW5hYmlsaXR5IHRvIHVzZSB0aGUgcHJv
-Z3JhbSAoaW5jbHVkaW5nIGJ1dCBub3QgbGltaXRlZAp0byBsb3NzIG9mIGRhdGEgb3IgZGF0YSBi
-ZWluZyByZW5kZXJlZCBpbmFjY3VyYXRlIG9yIGxvc3NlcyBzdXN0YWluZWQgYnkKeW91IG9yIHRo
-aXJkIHBhcnRpZXMgb3IgYSBmYWlsdXJlIG9mIHRoZSBwcm9ncmFtIHRvIG9wZXJhdGUgd2l0aCBh
-bnkgb3RoZXIKcHJvZ3JhbXMpLCBldmVuIGlmIHN1Y2ggaG9sZGVyIG9yIG90aGVyIHBhcnR5IGhh
-cyBiZWVuIGFkdmlzZWQgb2YgdGhlCnBvc3NpYmlsaXR5IG9mIHN1Y2ggZGFtYWdlcy59CgpcZW5k
-e2VudW1lcmF0ZX0KCgpcYmVnaW57Y2VudGVyfQp7XExhcmdlXHNjIEVuZCBvZiBUZXJtcyBhbmQg
-Q29uZGl0aW9uc30KXGVuZHtjZW50ZXJ9CgoKXHBhZ2VicmVha1syXQoKXHNlY3Rpb24qe0FwcGVu
-ZGl4OiBIb3cgdG8gQXBwbHkgVGhlc2UgVGVybXMgdG8gWW91ciBOZXcgUHJvZ3JhbXN9CgpJZiB5
-b3UgZGV2ZWxvcCBhIG5ldyBwcm9ncmFtLCBhbmQgeW91IHdhbnQgaXQgdG8gYmUgb2YgdGhlIGdy
-ZWF0ZXN0CnBvc3NpYmxlIHVzZSB0byB0aGUgcHVibGljLCB0aGUgYmVzdCB3YXkgdG8gYWNoaWV2
-ZSB0aGlzIGlzIHRvIG1ha2UgaXQKZnJlZSBzb2Z0d2FyZSB3aGljaCBldmVyeW9uZSBjYW4gcmVk
-aXN0cmlidXRlIGFuZCBjaGFuZ2UgdW5kZXIgdGhlc2UKdGVybXMuCgogIFRvIGRvIHNvLCBhdHRh
-Y2ggdGhlIGZvbGxvd2luZyBub3RpY2VzIHRvIHRoZSBwcm9ncmFtLiAgSXQgaXMgc2FmZXN0IHRv
-CiAgYXR0YWNoIHRoZW0gdG8gdGhlIHN0YXJ0IG9mIGVhY2ggc291cmNlIGZpbGUgdG8gbW9zdCBl
-ZmZlY3RpdmVseSBjb252ZXkKICB0aGUgZXhjbHVzaW9uIG9mIHdhcnJhbnR5OyBhbmQgZWFjaCBm
-aWxlIHNob3VsZCBoYXZlIGF0IGxlYXN0IHRoZQogIGBgY29weXJpZ2h0JycgbGluZSBhbmQgYSBw
-b2ludGVyIHRvIHdoZXJlIHRoZSBmdWxsIG5vdGljZSBpcyBmb3VuZC4KClxiZWdpbntxdW90ZX0K
-b25lIGxpbmUgdG8gZ2l2ZSB0aGUgcHJvZ3JhbSdzIG5hbWUgYW5kIGEgYnJpZWYgaWRlYSBvZiB3
-aGF0IGl0IGRvZXMuIFxcCkNvcHlyaWdodCAoQykgeXl5eSAgbmFtZSBvZiBhdXRob3IgXFwKClRo
-aXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQv
-b3IgbW9kaWZ5Cml0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExp
-Y2Vuc2UgYXMgcHVibGlzaGVkIGJ5CnRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhl
-ciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yCihhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVy
-IHZlcnNpb24uCgpUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBp
-dCB3aWxsIGJlIHVzZWZ1bCwKYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4g
-dGhlIGltcGxpZWQgd2FycmFudHkgb2YKTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEg
-UEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQpHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBm
-b3IgbW9yZSBkZXRhaWxzLgoKWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUg
-R05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5v
-dCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKRm91bmRhdGlvbiwgSW5jLiwgNTkgVGVtcGxl
-IFBsYWNlIC0gU3VpdGUgMzMwLCBCb3N0b24sIE1BICAwMjExMS0xMzA3LCBVU0EuClxlbmR7cXVv
-dGV9CgpBbHNvIGFkZCBpbmZvcm1hdGlvbiBvbiBob3cgdG8gY29udGFjdCB5b3UgYnkgZWxlY3Ry
-b25pYyBhbmQgcGFwZXIgbWFpbC4KCklmIHRoZSBwcm9ncmFtIGlzIGludGVyYWN0aXZlLCBtYWtl
-IGl0IG91dHB1dCBhIHNob3J0IG5vdGljZSBsaWtlIHRoaXMKd2hlbiBpdCBzdGFydHMgaW4gYW4g
-aW50ZXJhY3RpdmUgbW9kZToKClxiZWdpbntxdW90ZX0KR25vbW92aXNpb24gdmVyc2lvbiA2OSwg
-Q29weXJpZ2h0IChDKSB5eXl5ICBuYW1lIG9mIGF1dGhvciBcXApHbm9tb3Zpc2lvbiBjb21lcyB3
-aXRoIEFCU09MVVRFTFkgTk8gV0FSUkFOVFk7IGZvciBkZXRhaWxzIHR5cGUgYHNob3cgdycuIFxc
-ClRoaXMgaXMgZnJlZSBzb2Z0d2FyZSwgYW5kIHlvdSBhcmUgd2VsY29tZSB0byByZWRpc3RyaWJ1
-dGUgaXQKdW5kZXIgY2VydGFpbiBjb25kaXRpb25zOyB0eXBlIGBzaG93IGMnIGZvciBkZXRhaWxz
-LgpcZW5ke3F1b3RlfQoKClRoZSBoeXBvdGhldGljYWwgY29tbWFuZHMge1x0dCBzaG93IHd9IGFu
-ZCB7XHR0IHNob3cgY30gc2hvdWxkIHNob3cgdGhlCmFwcHJvcHJpYXRlIHBhcnRzIG9mIHRoZSBH
-ZW5lcmFsIFB1YmxpYyBMaWNlbnNlLiAgT2YgY291cnNlLCB0aGUgY29tbWFuZHMKeW91IHVzZSBt
-YXkgYmUgY2FsbGVkIHNvbWV0aGluZyBvdGhlciB0aGFuIHtcdHQgc2hvdyB3fSBhbmQge1x0dCBz
-aG93IGN9Owp0aGV5IGNvdWxkIGV2ZW4gYmUgbW91c2UtY2xpY2tzIG9yIG1lbnUgaXRlbXMtLS13
-aGF0ZXZlciBzdWl0cyB5b3VyCnByb2dyYW0uCgpZb3Ugc2hvdWxkIGFsc28gZ2V0IHlvdXIgZW1w
-bG95ZXIgKGlmIHlvdSB3b3JrIGFzIGEgcHJvZ3JhbW1lcikgb3IgeW91cgpzY2hvb2wsIGlmIGFu
-eSwgdG8gc2lnbiBhIGBgY29weXJpZ2h0IGRpc2NsYWltZXInJyBmb3IgdGhlIHByb2dyYW0sIGlm
-Cm5lY2Vzc2FyeS4gIEhlcmUgaXMgYSBzYW1wbGU7IGFsdGVyIHRoZSBuYW1lczoKClxiZWdpbntx
-dW90ZX0KWW95b2R5bmUsIEluYy4sIGhlcmVieSBkaXNjbGFpbXMgYWxsIGNvcHlyaWdodCBpbnRl
-cmVzdCBpbiB0aGUgcHJvZ3JhbSBcXApgR25vbW92aXNpb24nICh3aGljaCBtYWtlcyBwYXNzZXMg
-YXQgY29tcGlsZXJzKSB3cml0dGVuIGJ5IEphbWVzIEhhY2tlci4gXFwKCnNpZ25hdHVyZSBvZiBU
-eSBDb29uLCAxIEFwcmlsIDE5ODkgXFwKVHkgQ29vbiwgUHJlc2lkZW50IG9mIFZpY2UKXGVuZHtx
-dW90ZX0KCgpUaGlzIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZG9lcyBub3QgcGVybWl0IGluY29y
-cG9yYXRpbmcgeW91ciBwcm9ncmFtCmludG8gcHJvcHJpZXRhcnkgcHJvZ3JhbXMuICBJZiB5b3Vy
-IHByb2dyYW0gaXMgYSBzdWJyb3V0aW5lIGxpYnJhcnksIHlvdQptYXkgY29uc2lkZXIgaXQgbW9y
-ZSB1c2VmdWwgdG8gcGVybWl0IGxpbmtpbmcgcHJvcHJpZXRhcnkgYXBwbGljYXRpb25zCndpdGgg
-dGhlIGxpYnJhcnkuICBJZiB0aGlzIGlzIHdoYXQgeW91IHdhbnQgdG8gZG8sIHVzZSB0aGUgR05V
-IExpYnJhcnkKR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBpbnN0ZWFkIG9mIHRoaXMgTGljZW5zZS4K
-Cgo=
-" ++ Glurf ++ lists:reverse(Glurf++"kalle").
diff --git a/lib/compiler/test/compilation_SUITE_data/nested_tuples_in_case_expr.erl b/lib/compiler/test/compilation_SUITE_data/nested_tuples_in_case_expr.erl
deleted file mode 100644
index bbacb7cf14..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/nested_tuples_in_case_expr.erl
+++ /dev/null
@@ -1,37 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(nested_tuples_in_case_expr).
--export([nested_tuples_in_case_expr/0,t/2]).
-
-nested_tuples_in_case_expr() ->
- ok.
-
-t(A, B) ->
- case {{element(1, A),element(2, B)},{element(2, A),element(2, B)}} of
- {Same,Same} -> ok;
- {{0,1},{up,X}} -> bar(X);
- {_,{X,_}} -> bar(X)
- end.
-
-bar(X) -> X.
-
-
-
-
diff --git a/lib/compiler/test/compilation_SUITE_data/otp_2141.erl b/lib/compiler/test/compilation_SUITE_data/otp_2141.erl
deleted file mode 100644
index 4737d0972e..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/otp_2141.erl
+++ /dev/null
@@ -1,25 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_2141).
--export([otp_2141/0]).
-
-
-otp_2141() ->
- ok.
diff --git a/lib/compiler/test/compilation_SUITE_data/otp_2173.erl b/lib/compiler/test/compilation_SUITE_data/otp_2173.erl
deleted file mode 100644
index 5479700d1d..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/otp_2173.erl
+++ /dev/null
@@ -1,32 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_2173).
--compile(export_all).
-
--record(t, {a = fun(X) -> X*X end}).
-
-otp_2173() ->
- ok.
-
-t() ->
- #t{}.
-
-
-
diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5076.erl b/lib/compiler/test/compilation_SUITE_data/otp_5076.erl
deleted file mode 100644
index 19f974bb5b..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/otp_5076.erl
+++ /dev/null
@@ -1,28 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_5076).
--export([?MODULE/0]).
-
-?MODULE() ->
- [] = t(),
- ok.
-
-t() ->
- [3 || {3=4} <- []].
diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5092.erl b/lib/compiler/test/compilation_SUITE_data/otp_5092.erl
deleted file mode 100644
index 88c4519cc6..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/otp_5092.erl
+++ /dev/null
@@ -1,40 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_5092).
--export([?MODULE/0]).
-
-?MODULE() ->
- [] = t(),
- [] = t2(),
- [t] = t4(),
- [] = t5(),
- ok.
-
-t() ->
- [t || {C=D}={_,_} <- []].
-
-t2() ->
- [X || {X,{Y}={X,X}} <- []].
-
-t4() ->
- [t || "a"++"b" = "ab" <- ["ab"]].
-
-t5() ->
- [{X,Y} || {X} <- [], begin Y = X, Y =:= X end].
diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5244.erl b/lib/compiler/test/compilation_SUITE_data/otp_5244.erl
deleted file mode 100644
index 8144e5a225..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/otp_5244.erl
+++ /dev/null
@@ -1,48 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_5244).
--export([?MODULE/0]).
-
-?MODULE() ->
- L = [{stretch,0,0},
- {bad,[]},
- {bad,atom},
- {bad,0},
- {bad,16#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA},
- {bad,16#555555555555555555555555555555555555555555555555555}],
- remove_failure(L, unit, 0).
-
-remove_failure([], _Unit, _MaxFailure) ->
- ok;
-remove_failure([{bad,Bad}|_], _Unit, _MaxFailure) ->
- Bad;
-remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) ->
- {MinMax,NewMaxFailure} = max_failure(),
- case {MinMax,remove_failure(Specs, Unit, NewMaxFailure)} of
- {min,{NewMaxFailure,Rest}} ->
- {done,[{fixed,Mi} | Rest]};
- {min,_} when Specs =/= [] ->
- remove_failure([Stretch|tl(Specs)], Unit, NewMaxFailure);
- {min,_} ->
- ok
- end.
-
-max_failure() ->
- {min,1}.
diff --git a/lib/compiler/test/compilation_SUITE_data/otp_6121a.erl b/lib/compiler/test/compilation_SUITE_data/otp_6121a.erl
deleted file mode 100644
index a8fc5f3bb5..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/otp_6121a.erl
+++ /dev/null
@@ -1,33 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_6121a).
--export([?MODULE/0]).
-
-%% Thanks to Martin Bjorklund.
-
-?MODULE() ->
- G = fun() -> ok end,
- try
- fun() -> ok end
- after
- fun({A, B}) -> A + B end
- end,
- ok.
-
diff --git a/lib/compiler/test/compilation_SUITE_data/otp_6121b.erl b/lib/compiler/test/compilation_SUITE_data/otp_6121b.erl
deleted file mode 100644
index df2e60deb5..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/otp_6121b.erl
+++ /dev/null
@@ -1,34 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_6121b).
--export([?MODULE/0]).
-
-%% Thanks to Tim Rath.
-
-?MODULE() ->
- A = {6},
- try
- io:fwrite("")
- after
- fun () ->
- fun () -> {B} = A end
- end
- end.
-
diff --git a/lib/compiler/test/compilation_SUITE_data/pattern_expr.erl b/lib/compiler/test/compilation_SUITE_data/pattern_expr.erl
deleted file mode 100644
index a78b3501d1..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/pattern_expr.erl
+++ /dev/null
@@ -1,31 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(pattern_expr).
-
--export(pattern_expr/0).
-
-pattern_expr() ->
- f().
-
-f() ->
- case 4 of
- 2+2 ->
- ok
- end.
diff --git a/lib/compiler/test/compilation_SUITE_data/trycatch_4.erl b/lib/compiler/test/compilation_SUITE_data/trycatch_4.erl
deleted file mode 100644
index f465376bdc..0000000000
--- a/lib/compiler/test/compilation_SUITE_data/trycatch_4.erl
+++ /dev/null
@@ -1,51 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(trycatch_4).
--export([trycatch_4/0]).
--record(state, {foo}).
-
-trycatch_4() ->
- handle_info({foo}, #state{}),
- ok.
-
-handle_info({_}, State) ->
- foo(),
- State#state{foo = bar},
- case ok of
- _ ->
- case catch foo() of
- ok ->
- {stop, State}
- end
- end;
-handle_info(_, State) ->
- (catch begin
- foo(),
- State#state{foo = bar}
- end),
- case ok of
- _ ->
- case catch foo() of
- ok ->
- {stop, State}
- end
- end.
-
-foo() -> ok.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index e1db99b357..e634f0fcc2 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -31,13 +31,12 @@
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, encrypted_abstr/1,
strict_record/1,
- missing_testheap/1, cover/1, env/1, core/1, asm/1,
+ cover/1, env/1, core/1,
+ core_roundtrip/1, asm/1,
sys_pre_attributes/1, dialyzer/1,
warnings/1, pre_load_check/1
]).
--export([init/3]).
-
suite() -> [{ct_hooks,[ts_install_cth]}].
%% To cover the stripping of 'type' and 'spec' in beam_asm.
@@ -50,7 +49,7 @@ all() ->
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, encrypted_abstr,
strict_record,
- missing_testheap, cover, env, core, asm,
+ cover, env, core, core_roundtrip, asm,
sys_pre_attributes, dialyzer, warnings, pre_load_check].
groups() ->
@@ -545,7 +544,6 @@ verify_abstract(Target) ->
has_crypto() ->
try
crypto:start(),
- <<_,_,_,_,_>> = crypto:rand_bytes(5),
crypto:stop(),
true
catch
@@ -649,46 +647,6 @@ test_sloppy() ->
{1,2} = record_access:test(Turtle),
Turtle.
-missing_testheap(Config) when is_list(Config) ->
- DataDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- Opts = [{outdir,PrivDir}],
- OldPath = code:get_path(),
- try
- code:add_patha(PrivDir),
- c:c(filename:join(DataDir, "missing_testheap1"), Opts),
- c:c(filename:join(DataDir, "missing_testheap2"), Opts),
- ok = test(fun() ->
- missing_testheap1:f({a,self()},{state,true,b})
- end, {a,b}),
- ok = test(fun() ->
- missing_testheap2:f({a,self()},16#80000000) end,
- bigger)
- after
- code:set_path(OldPath),
- file:delete(filename:join(PrivDir, "missing_testheap1.beam")),
- file:delete(filename:join(PrivDir, "missing_testheap2.beam"))
- end,
- ok.
-
-test(Fun, Result) ->
- test(500, Fun, Result, []).
-
-test(0, _, _, _) ->
- ok;
-test(Iter, Fun, Result, Filler) ->
- spawn(?MODULE, init, [self(), Fun, list_to_tuple(Filler)]),
- receive
- {result, Result} ->
- test(Iter-1, Fun, Result, [0|Filler]);
- {result, Other} ->
- io:format("Expected ~p; got ~p~n", [Result, Other]),
- ct:fail(failed)
- end.
-
-init(ReplyTo, Fun, _Filler) ->
- ReplyTo ! {result, Fun()}.
-
env(Config) when is_list(Config) ->
{Simple,Target} = get_files(Config, simple, env),
{ok,Cwd} = file:get_cwd(),
@@ -790,6 +748,120 @@ compile_forms(Forms, Opts) ->
Other -> throw({error,Other})
end.
+%% Pretty-print core and read it back. Should be identical.
+
+core_roundtrip(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)),
+ ok = file:make_dir(Outdir),
+
+ Wc = filename:join(filename:dirname(code:which(?MODULE)), "*.beam"),
+ TestBeams = filelib:wildcard(Wc),
+ test_lib:p_run(fun(F) -> do_core_roundtrip(F, Outdir) end, TestBeams).
+
+do_core_roundtrip(Beam, Outdir) ->
+ try
+ {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} =
+ beam_lib:chunks(Beam, [abstract_code]),
+ do_core_roundtrip_1(Mod, Abstr, Outdir)
+ catch
+ throw:{error,Error} ->
+ io:format("*** compilation failure '~p' for file ~s\n",
+ [Error,Beam]),
+ error;
+ Class:Error ->
+ io:format("~p: ~p ~p\n~p\n",
+ [Beam,Class,Error,erlang:get_stacktrace()]),
+ error
+ end.
+
+do_core_roundtrip_1(Mod, Abstr, Outdir) ->
+ {ok,Mod,Core0} = compile:forms(Abstr, [to_core0]),
+ do_core_roundtrip_2(Mod, Core0, Outdir),
+
+ %% Primarily, test that annotations are accepted for all
+ %% constructs. Secondarily, smoke test cerl_trees:label/1.
+ {Core,_} = cerl_trees:label(Core0),
+ do_core_roundtrip_2(Mod, Core, Outdir).
+
+do_core_roundtrip_2(M, Core0, Outdir) ->
+ CoreFile = filename:join(Outdir, atom_to_list(M)++".core"),
+ CorePP = core_pp:format_all(Core0),
+ ok = file:write_file(CoreFile, CorePP),
+
+ %% Parse the .core file and return the result as Core Erlang Terms.
+ Core2 = case compile:file(CoreFile, [report_errors,from_core,
+ no_copt,to_core,binary]) of
+ {ok,M,Core1} -> Core1;
+ Other -> throw({error,Other})
+ end,
+ Core = undo_var_translation(Core2),
+ ok = file:delete(CoreFile),
+
+ case cmp_core(Core0, Core, M) of
+ true -> ok;
+ false -> error
+ end,
+
+ ok.
+
+undo_var_translation(Tree) ->
+ F = fun(Node) ->
+ case cerl:is_c_var(Node) of
+ true ->
+ Name0 = cerl:var_name(Node),
+ try atom_to_list(Name0) of
+ "_X"++Name ->
+ cerl:update_c_var(Node, list_to_atom(Name));
+ "_"++Name ->
+ cerl:update_c_var(Node, list_to_atom(Name));
+ _ ->
+ Node
+ catch
+ error:badarg ->
+ Node
+
+ end;
+ false ->
+ Node
+ end
+ end,
+ cerl_trees:map(F, Tree).
+
+cmp_core(E, E, _Mod) ->
+ true;
+cmp_core(M1, M2, Mod) ->
+ cmp_core_fs(cerl:module_defs(M1), cerl:module_defs(M2), Mod).
+
+cmp_core_fs([F1|T1], [F2|T2], Mod) ->
+ cmp_core_f(F1, F2, Mod) andalso cmp_core_fs(T1, T2, Mod);
+cmp_core_fs([], [], _Mod) ->
+ true;
+cmp_core_fs(_, _, _Mod) ->
+ false.
+
+cmp_core_f(E, E, _Mod) ->
+ true;
+cmp_core_f({Name,F1}, {Name,F2}, Mod) ->
+ case diff(F1, F2) of
+ F1 ->
+ true;
+ Diff ->
+ io:format("~p ~p:\n~p\n", [Mod,Name,Diff]),
+ false
+ end.
+
+diff(E, E) ->
+ E;
+diff([H1|T1], [H2|T2]) ->
+ [diff(H1, H2)|diff(T1, T2)];
+diff(T1, T2) when tuple_size(T1) =:= tuple_size(T2) ->
+ L = diff(tuple_to_list(T1), tuple_to_list(T2)),
+ list_to_tuple(L);
+diff(E1, E2) ->
+ {'DIFF',E1,E2}.
+
+
%% Compile to Beam assembly language (.S) and then try to
%% run .S through the compiler again.
diff --git a/lib/compiler/test/compile_SUITE_data/missing_testheap1.erl b/lib/compiler/test/compile_SUITE_data/missing_testheap1.erl
deleted file mode 100644
index 2c1eb8a3ae..0000000000
--- a/lib/compiler/test/compile_SUITE_data/missing_testheap1.erl
+++ /dev/null
@@ -1,36 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(missing_testheap1).
-
--compile(export_all).
-%%-export([Function/Arity, ...]).
--record(state,{e1,e2}).
-
-f({a,DpId},State) when State ==
-#state{e1=true,
- e2=a} ->
- {a,a};
-
-f({a,DpId},State) when State ==
-#state{e1=true,
- e2=b} ->
- {a,b}.
-
-
diff --git a/lib/compiler/test/compile_SUITE_data/missing_testheap2.erl b/lib/compiler/test/compile_SUITE_data/missing_testheap2.erl
deleted file mode 100644
index b2aa9b5a5a..0000000000
--- a/lib/compiler/test/compile_SUITE_data/missing_testheap2.erl
+++ /dev/null
@@ -1,30 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(missing_testheap2).
-
--compile(export_all).
-
-f({a,DpId},16#7fffffff) ->
- big;
-
-f({a,DpId},16#80000000) ->
- bigger.
-
-
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index ffbf59f0ee..13d274d98a 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -28,7 +28,7 @@
or_guard/1,more_or_guards/1,
complex_or_guards/1,and_guard/1,
xor_guard/1,more_xor_guards/1,
- old_guard_tests/1,
+ old_guard_tests/1,complex_guard/1,
build_in_guard/1,gbif/1,
t_is_boolean/1,is_function_2/1,
tricky/1,rel_ops/1,rel_op_combinations/1,literal_type_tests/1,
@@ -48,7 +48,8 @@ groups() ->
[misc,const_cond,basic_not,complex_not,nested_nots,
semicolon,complex_semicolon,comma,or_guard,
more_or_guards,complex_or_guards,and_guard,xor_guard,
- more_xor_guards,build_in_guard,old_guard_tests,gbif,
+ more_xor_guards,build_in_guard,
+ old_guard_tests,complex_guard,gbif,
t_is_boolean,is_function_2,tricky,
rel_ops,rel_op_combinations,
literal_type_tests,basic_andalso_orelse,traverse_dcd,
@@ -947,6 +948,26 @@ og(_) -> what.
on(V) when number(V) -> number;
on(_) -> not_number.
+complex_guard(_Config) ->
+ _ = [true = do_complex_guard(X, Y, Z) ||
+ X <- [4,5], Y <- [4,5], Z <- [4,5]],
+ _ = [true = do_complex_guard(X, Y, Z) ||
+ X <- [1,2,3], Y <- [1,2,3], Z <- [1,2,3]],
+ _ = [catch do_complex_guard(X, Y, Z) ||
+ X <- [1,2,3,4,5], Y <- [0,6], Z <- [1,2,3,4,5]],
+ ok.
+
+do_complex_guard(X1, Y1, Z1) ->
+ if
+ ((X1 =:= 4) or (X1 =:= 5)) and
+ ((Y1 =:= 4) or (Y1 =:= 5)) and
+ ((Z1 =:= 4) or (Z1 =:= 5)) or
+ ((X1 =:= 1) or (X1 =:= 2) or (X1 =:= 3)) and
+ ((Y1 =:= 1) or (Y1 =:= 2) or (Y1 =:= 3)) and
+ ((Z1 =:= 1) or (Z1 =:= 2) or (Z1 =:= 3)) ->
+ true
+ end.
+
gbif(Config) when is_list(Config) ->
error = gbif_1(1, {false,true}),
ok = gbif_1(2, {false,true}),
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index dd0bcb4245..3cb49433ce 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -89,6 +89,18 @@ basic(Config) when is_list(Config) ->
%% Filter expressions with andalso/orelse.
"abc123" = alphanum("?abc123.;"),
+ %% Aliased patterns.
+ [] = [t || {C=D}={_,_} <- []],
+ [] = [X || {X,{Y}={X,X}} <- []],
+ [t] = [t || "a"++"b" = "ab" <- ["ab"]],
+
+ %% Strange filter block.
+ [] = [{X,Y} || {X} <- [], begin Y = X, Y =:= X end],
+ [{a,a}] = [{X,Y} || {X} <- [{a}], begin Y = X, Y =:= X end],
+
+ %% Not matching.
+ [] = [3 || {3=4} <- []],
+
%% Error cases.
[] = [{xx,X} || X <- L0, element(2, X) == no_no_no],
{'EXIT',_} = (catch [X || X <- L1, list_to_atom(X) == dum]),
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index f6a24b3211..92a9802cad 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -24,7 +24,7 @@
pmatch/1,mixed/1,aliases/1,match_in_call/1,
untuplify/1,shortcut_boolean/1,letify_guard/1,
selectify/1,underscore/1,match_map/1,map_vars_used/1,
- coverage/1]).
+ coverage/1,grab_bag/1]).
-include_lib("common_test/include/ct.hrl").
@@ -38,7 +38,8 @@ groups() ->
[{p,[parallel],
[pmatch,mixed,aliases,match_in_call,untuplify,
shortcut_boolean,letify_guard,selectify,
- underscore,match_map,map_vars_used,coverage]}].
+ underscore,match_map,map_vars_used,coverage,
+ grab_bag]}].
init_per_suite(Config) ->
@@ -93,9 +94,9 @@ mixit(X) ->
2 -> b;
3 -> 42;
4 -> 77;
- 5 -> blurf;
- 6 -> 87987987;
- 7 -> {a,b,c}
+ 4+1 -> blurf;
+ 5+1 -> 87987987;
+ 6+1 -> {a,b,c}
end of
a -> glufs;
b -> klafs;
@@ -149,6 +150,9 @@ aliases(Config) when is_list(Config) ->
none = mixed_aliases({a,42}),
none = mixed_aliases(42),
+ %% Non-matching aliases.
+ {'EXIT',{{badmatch,42},_}} = (catch nomatch_alias(42)),
+
ok.
str_alias(V) ->
@@ -258,6 +262,10 @@ mixed_aliases(<<X:8>> = {a,X}) -> {c,X};
mixed_aliases([X] = <<X:8>>) -> {d,X};
mixed_aliases(_) -> none.
+nomatch_alias(I) ->
+ {ok={A,B}} = id(I),
+ {A,B}.
+
%% OTP-7018.
match_in_call(Config) when is_list(Config) ->
@@ -465,4 +473,53 @@ coverage_2(2, b, x) -> ok.
coverage_3([$a]++[]++"bc") -> ok.
+grab_bag(_Config) ->
+ [_|T] = id([a,b,c]),
+ [b,c] = id(T),
+
+ T1 = fun() ->
+ [_|_] = x
+ end,
+ {'EXIT',_} = (catch T1()),
+
+ T2 = fun(A, B) ->
+ case {{element(1, A),element(2, B)},
+ {element(2, A),element(2, B)}} of
+ {Same,Same} -> ok;
+ {{0,1},{up,X}} -> id(X);
+ {_,{X,_}} -> id(X)
+ end
+ end,
+ ok = T2({a,a,z,z}, {z,a,z}),
+ 1 = T2({0,up}, {zzz,1}),
+ y = T2({x,y}, {a,z,z}),
+
+ %% OTP-5244.
+ L = [{stretch,0,0},
+ {bad,[]},
+ {bad,atom},
+ {bad,0},
+ {bad,16#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA},
+ {bad,16#555555555555555555555555555555555555555555555555555}],
+ ok = grab_bag_remove_failure(L, unit, 0),
+
+ ok.
+
+grab_bag_remove_failure([], _Unit, _MaxFailure) ->
+ ok;
+grab_bag_remove_failure([{bad,Bad}|_], _Unit, _MaxFailure) ->
+ Bad;
+grab_bag_remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) ->
+ {MinMax,NewMaxFailure} = id({min,1}),
+ case {MinMax,grab_bag_remove_failure(Specs, Unit, NewMaxFailure)} of
+ {min,{NewMaxFailure,Rest}} ->
+ {done,[{fixed,Mi} | Rest]};
+ {min,_} when Specs =/= [] ->
+ grab_bag_remove_failure([Stretch|tl(Specs)], Unit, NewMaxFailure);
+ {min,_} ->
+ ok
+ end.
+
+
+
id(I) -> I.
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index 8f9ed685bf..f05fe6c943 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -23,7 +23,8 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
tobias/1,empty_string/1,md5/1,silly_coverage/1,
- confused_literals/1,integer_encoding/1,override_bif/1]).
+ confused_literals/1,integer_encoding/0,integer_encoding/1,
+ override_bif/1]).
-include_lib("common_test/include/ct.hrl").
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index 4f048f5080..5546765f26 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -28,7 +28,7 @@
init_per_testcase/2,end_per_testcase/2,
errors/1,record_test_2/1,record_test_3/1,record_access_in_guards/1,
guard_opt/1,eval_once/1,foobar/1,missing_test_heap/1,
- nested_access/1,coverage/1]).
+ nested_access/1,coverage/1,grab_bag/1]).
init_per_testcase(_Case, Config) ->
Config.
@@ -48,7 +48,7 @@ groups() ->
[{p,test_lib:parallel(),
[errors,record_test_2,record_test_3,
record_access_in_guards,guard_opt,eval_once,foobar,
- missing_test_heap,nested_access,coverage]}].
+ missing_test_heap,nested_access,coverage,grab_bag]}].
init_per_suite(Config) ->
@@ -601,4 +601,60 @@ coverage(Config) when is_list(Config) ->
#rr{a=1,b=2,c=42} = id(R), %Test for correctness.
ok.
+
+-record(default_fun, {a = fun(X) -> X*X end}).
+
+%% compiler treats records with 1 and 2 fields differently...
+-record(gb_nil, {}).
+-record(gb_foo, {hello=1}).
+-record(gb_bar, {hello=2,there=3}).
+
+%% Taken from compilation_SUITE.
+grab_bag(_Config) ->
+ T1 = fun() ->
+ X = #foo{},
+ Y = #foo{},
+ {X#foo.a == Y#foo.a,X#foo.b}
+ end,
+ {true,undefined} = T1(),
+
+ T2 = fun(X, Y) ->
+ first_arg(X#foo.a =/= Y#foo.a, X#foo.b =/= X#foo.b)
+ end,
+ true = T2(#foo{a=x,b=z}, #foo{a=y,b=z}),
+
+ T3 = fun() ->
+ #default_fun{a=Fun} = id(#default_fun{}),
+ 9 = Fun(3)
+ end,
+ T3(),
+
+ %% Stupid code, but the compiler used to crash.
+ T4 = fun() ->
+ F0 = fun() ->
+ R1 = #gb_nil{},
+ R2 = R1#gb_nil{},
+ R1 = R2
+ end,
+ F1 = fun() ->
+ R1 = #gb_foo{},
+ R2 = R1#gb_foo{},
+ R1 = R2
+ end,
+
+ F2 = fun() ->
+ R1 = #gb_bar{},
+ R2 = R1#gb_bar{},
+ R1 = R2
+ end,
+ F0(),
+ F1(),
+ F2()
+ end,
+ T4(),
+
+ ok.
+
+first_arg(First, _) -> First.
+
id(I) -> I.
diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl
index 5e0bb6d00a..f7ad78cb8d 100644
--- a/lib/compiler/test/trycatch_SUITE.erl
+++ b/lib/compiler/test/trycatch_SUITE.erl
@@ -26,7 +26,7 @@
nested_of/1,nested_catch/1,nested_after/1,
nested_horrid/1,last_call_optimization/1,bool/1,
plain_catch_coverage/1,andalso_orelse/1,get_in_try/1,
- hockey/1]).
+ hockey/1,handle_info/1,catch_in_catch/1,grab_bag/1]).
-include_lib("common_test/include/ct.hrl").
@@ -42,7 +42,7 @@ groups() ->
after_oops,eclectic,rethrow,nested_of,nested_catch,
nested_after,nested_horrid,last_call_optimization,
bool,plain_catch_coverage,andalso_orelse,get_in_try,
- hockey]}].
+ hockey,handle_info,catch_in_catch,grab_bag]}].
init_per_suite(Config) ->
@@ -919,8 +919,6 @@ andalso_orelse_1(A, B) ->
catched
end,B}.
-id(I) -> I.
-
andalso_orelse_2({Type,Keyval}) ->
try
if is_atom(Type) andalso length(Keyval) > 0 -> ok;
@@ -957,3 +955,86 @@ hockey() ->
receive _ -> (b = fun() -> ok end)
+ hockey, +x after 0 -> ok end, try (a = fun() -> ok end) + hockey, +
y catch _ -> ok end.
+
+
+-record(state, {foo}).
+
+handle_info(_Config) ->
+ do_handle_info({foo}, #state{}),
+ ok.
+
+do_handle_info({_}, State) ->
+ handle_info_ok(),
+ State#state{foo = bar},
+ case ok of
+ _ ->
+ case catch handle_info_ok() of
+ ok ->
+ {stop, State}
+ end
+ end;
+do_handle_info(_, State) ->
+ (catch begin
+ handle_info_ok(),
+ State#state{foo = bar}
+ end),
+ case ok of
+ _ ->
+ case catch handle_info_ok() of
+ ok ->
+ {stop, State}
+ end
+ end.
+
+handle_info_ok() -> ok.
+
+'catch_in_catch'(_Config) ->
+ process_flag(trap_exit, true),
+ Pid = spawn_link(fun() ->
+ catch_in_catch_init(x),
+ exit(good_exit)
+ end),
+ receive
+ {'EXIT',Pid,good_exit} ->
+ ok;
+ Other ->
+ io:format("Unexpected: ~p\n", [Other]),
+ error
+ after 32000 ->
+ io:format("No message received\n"),
+ error
+ end.
+
+'catch_in_catch_init'(Param) ->
+ process_flag(trap_exit, true),
+ %% The catches were improperly nested, causing a "No catch found" crash.
+ (catch begin
+ id(Param),
+ (catch exit(bar))
+ end
+ ),
+ ignore.
+
+grab_bag(_Config) ->
+ %% Thanks to Martin Bjorklund.
+ _ = fun() -> ok end,
+ try
+ fun() -> ok end
+ after
+ fun({A, B}) -> A + B end
+ end,
+
+ %% Thanks to Tim Rath.
+ A = {6},
+ try
+ io:fwrite("")
+ after
+ fun () ->
+ fun () -> {_} = A end
+ end
+ end,
+
+ ok.
+
+
+id(I) -> I.
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 1be22a0b8a..067e220863 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -214,7 +214,6 @@ static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_
static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM strong_rand_mpint_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -285,7 +284,6 @@ static ErlNifFunc nif_funcs[] = {
{"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt},
{"rand_bytes", 1, rand_bytes_1},
{"strong_rand_bytes_nif", 1, strong_rand_bytes_nif},
- {"rand_bytes", 3, rand_bytes_3},
{"strong_rand_mpint_nif", 3, strong_rand_mpint_nif},
{"rand_uniform_nif", 2, rand_uniform_nif},
{"mod_exp_nif", 4, mod_exp_nif},
@@ -1927,27 +1925,7 @@ static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NI
return ret;
}
-static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Bytes, TopMask, BottomMask) */
- unsigned bytes;
- unsigned char* data;
- unsigned top_mask, bot_mask;
- ERL_NIF_TERM ret;
- if (!enif_get_uint(env, argv[0], &bytes)
- || !enif_get_uint(env, argv[1], &top_mask)
- || !enif_get_uint(env, argv[2], &bot_mask)) {
- return enif_make_badarg(env);
- }
- data = enif_make_new_binary(env, bytes, &ret);
- RAND_pseudo_bytes(data, bytes);
- ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes);
- if (bytes > 0) {
- data[bytes-1] |= top_mask;
- data[0] |= bot_mask;
- }
- return ret;
-}
static ERL_NIF_TERM strong_rand_mpint_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Bytes, TopMask, BottomMask) */
unsigned bits;
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index e0b989436f..5a5627747c 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -308,6 +308,8 @@
<desc>
<p>Generates public keys of type <c>Type</c>.
See also <seealso marker="public_key:public_key#generate_key-1">public_key:generate_key/1</seealso>
+ May throw exception <c>low_entropy</c> in case the random generator
+ failed due to lack of secure "randomness".
</p>
</desc>
</func>
@@ -596,22 +598,6 @@
</func>
<func>
- <name>rand_bytes(N) -> binary()</name>
- <fsummary>Generate a binary of random bytes</fsummary>
- <type>
- <v>N = integer()</v>
- </type>
- <desc>
- <p>Generates N bytes randomly uniform 0..255, and returns the
- result in a binary. Uses the <c>crypto</c> library pseudo-random
- number generator.</p>
- <p>This function is not recommended for cryptographic purposes.
- Please use <seealso marker="#strong_rand_bytes/1">
- strong_rand_bytes/1</seealso> instead.</p>
- </desc>
- </func>
-
- <func>
<name>rand_seed(Seed) -> ok</name>
<fsummary>Set the seed for random bytes generation</fsummary>
<type>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index a154476560..025d57e9c5 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -28,7 +28,7 @@
-export([generate_key/2, generate_key/3, compute_key/4]).
-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
-export([exor/2, strong_rand_bytes/1, mod_pow/3]).
--export([rand_bytes/1, rand_bytes/3, rand_uniform/2]).
+-export([rand_uniform/2]).
-export([block_encrypt/3, block_decrypt/3, block_encrypt/4, block_decrypt/4]).
-export([next_iv/2, next_iv/3]).
-export([stream_init/2, stream_init/3, stream_encrypt/2, stream_decrypt/2]).
@@ -39,6 +39,9 @@
-export([rand_seed/1]).
%% DEPRECATED
+-export([rand_bytes/1]).
+-deprecated({rand_bytes, 1, next_major_release}).
+
%% Replaced by hash_*
-export([md4/1, md4_init/0, md4_update/2, md4_final/1]).
-export([md5/1, md5_init/0, md5_update/2, md5_final/1]).
@@ -407,8 +410,6 @@ strong_rand_bytes(Bytes) ->
end.
strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
-rand_bytes(_Bytes, _Topmask, _Bottommask) -> ?nif_stub.
-
rand_uniform(From,To) when is_binary(From), is_binary(To) ->
case rand_uniform_nif(From,To) of
@@ -546,7 +547,7 @@ generate_key(dh, DHParameters, PrivateKey) ->
generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, PrivArg)
when is_binary(Verifier), is_binary(Generator), is_binary(Prime), is_atom(Version) ->
Private = case PrivArg of
- undefined -> random_bytes(32);
+ undefined -> strong_rand_bytes(32);
_ -> ensure_int_as_bin(PrivArg)
end,
host_srp_gen_key(Private, Verifier, Generator, Prime, Version);
@@ -554,7 +555,7 @@ generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, PrivArg)
generate_key(srp, {user, [Generator, Prime, Version]}, PrivateArg)
when is_binary(Generator), is_binary(Prime), is_atom(Version) ->
Private = case PrivateArg of
- undefined -> random_bytes(32);
+ undefined -> strong_rand_bytes(32);
_ -> PrivateArg
end,
user_srp_gen_key(Private, Generator, Prime);
@@ -606,16 +607,6 @@ compute_key(ecdh, Others, My, Curve) ->
nif_curve_params(Curve),
ensure_int_as_bin(My)).
-
-random_bytes(N) ->
- try strong_rand_bytes(N) of
- RandBytes ->
- RandBytes
- catch
- error:low_entropy ->
- rand_bytes(N)
- end.
-
%%--------------------------------------------------------------------
%%% On load
%%--------------------------------------------------------------------
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 0d18cd8017..6732f27824 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -269,7 +269,6 @@ rand_uniform() ->
[{doc, "rand_uniform and random_bytes testing"}].
rand_uniform(Config) when is_list(Config) ->
rand_uniform_aux_test(10),
- 10 = byte_size(crypto:rand_bytes(10)),
10 = byte_size(crypto:strong_rand_bytes(10)).
%%--------------------------------------------------------------------
@@ -649,8 +648,8 @@ ipow(A, B, M, Prod) ->
do_exor(B) ->
Z1 = zero_bin(B),
Z1 = crypto:exor(B, B),
- B1 = crypto:rand_bytes(100),
- B2 = crypto:rand_bytes(100),
+ B1 = crypto:strong_rand_bytes(100),
+ B2 = crypto:strong_rand_bytes(100),
Z2 = zero_bin(B1),
Z2 = crypto:exor(B1, B1),
Z2 = crypto:exor(B2, B2),
diff --git a/lib/crypto/test/old_crypto_SUITE.erl b/lib/crypto/test/old_crypto_SUITE.erl
index f57e9ff341..0d97290d10 100644
--- a/lib/crypto/test/old_crypto_SUITE.erl
+++ b/lib/crypto/test/old_crypto_SUITE.erl
@@ -2068,8 +2068,8 @@ exor_test(Config) when is_list(Config) ->
B = <<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>>,
Z1 = zero_bin(B),
Z1 = crypto:exor(B, B),
- B1 = crypto:rand_bytes(100),
- B2 = crypto:rand_bytes(100),
+ B1 = crypto:strong_rand_bytes(100),
+ B2 = crypto:strong_rand_bytes(100),
Z2 = zero_bin(B1),
Z2 = crypto:exor(B1, B1),
Z2 = crypto:exor(B2, B2),
diff --git a/lib/dialyzer/README b/lib/dialyzer/README
index 82d0f5ec48..951a92469b 100644
--- a/lib/dialyzer/README
+++ b/lib/dialyzer/README
@@ -6,7 +6,7 @@
## Copyright: Held by the authors; all rights reserved (2004 - 2010).
##----------------------------------------------------------------------------
-The DIALYZER, a DIscrepany AnaLYZer for ERlang programs.
+The DIALYZER, a DIscrepancy AnaLYZer for ERlang programs.
-----------------------------------------------
diff --git a/lib/dialyzer/doc/about.txt b/lib/dialyzer/doc/about.txt
index 7d9d819731..1318d9a65e 100644
--- a/lib/dialyzer/doc/about.txt
+++ b/lib/dialyzer/doc/about.txt
@@ -1,5 +1,5 @@
This is DIALYZER version 2.1.0
- DIALYZER is a DIscrepany AnaLYZer for ERlang programs.
+ DIALYZER is a DIscrepancy AnaLYZer for ERlang programs.
Copyright (C) Tobias Lindahl <[email protected]>
Kostis Sagonas <[email protected]>
diff --git a/lib/dialyzer/doc/manual.txt b/lib/dialyzer/doc/manual.txt
index 29c9518d84..be1fd2f8bc 100644
--- a/lib/dialyzer/doc/manual.txt
+++ b/lib/dialyzer/doc/manual.txt
@@ -4,7 +4,7 @@
## Kostis Sagonas <[email protected]>
##----------------------------------------------------------------------------
-The DIALYZER, a DIscrepany AnaLYZer for ERlang programs.
+The DIALYZER, a DIscrepancy AnaLYZer for ERlang programs.
-----------------------------------------------
diff --git a/lib/dialyzer/info b/lib/dialyzer/info
index 9fba4b54ad..b9ea26b712 100644
--- a/lib/dialyzer/info
+++ b/lib/dialyzer/info
@@ -1,2 +1,2 @@
group: tools
-short: The DIALYZER, a DIscrepany AnaLYZer for ERlang programs.
+short: The DIALYZER, a DIscrepancy AnaLYZer for ERlang programs.
diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src
index 003d7f2ba4..5b28f7ae86 100644
--- a/lib/dialyzer/src/dialyzer.app.src
+++ b/lib/dialyzer/src/dialyzer.app.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -47,6 +47,6 @@
{registered, []},
{applications, [compiler, hipe, kernel, stdlib, wx]},
{env, []},
- {runtime_dependencies, ["wx-1.2","syntax_tools-1.6.14","stdlib-2.5",
- "kernel-3.0","hipe-3.13","erts-7.0",
- "compiler-5.0"]}]}.
+ {runtime_dependencies, ["wx-1.2","syntax_tools-2.0","stdlib-3.0",
+ "kernel-5.0","hipe-3.15.1","erts-8.0",
+ "compiler-7.0"]}]}.
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 9f51dfe356..bcac8afe64 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -336,6 +336,9 @@ message_to_string({guard_fail, []}) ->
"Clause guard cannot succeed.\n";
message_to_string({guard_fail, [Arg1, Infix, Arg2]}) ->
io_lib:format("Guard test ~s ~s ~s can never succeed\n", [Arg1, Infix, Arg2]);
+message_to_string({map_update, [Type, Key]}) ->
+ io_lib:format("A key of type ~s cannot exist "
+ "in a map of type ~s\n", [Key, Type]);
message_to_string({neg_guard_fail, [Arg1, Infix, Arg2]}) ->
io_lib:format("Guard test not(~s ~s ~s) can never succeed\n",
[Arg1, Infix, Arg2]);
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index 601e2e954b..ea6a71217c 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -60,6 +60,7 @@
-define(WARN_BEHAVIOUR, warn_behaviour).
-define(WARN_UNDEFINED_CALLBACK, warn_undefined_callbacks).
-define(WARN_UNKNOWN, warn_unknown).
+-define(WARN_MAP_CONSTRUCTION, warn_map_construction).
%%
%% The following type has double role:
@@ -75,7 +76,8 @@
| ?WARN_CONTRACT_SUPERTYPE | ?WARN_CALLGRAPH
| ?WARN_UNMATCHED_RETURN | ?WARN_RACE_CONDITION
| ?WARN_BEHAVIOUR | ?WARN_CONTRACT_RANGE
- | ?WARN_UNDEFINED_CALLBACK | ?WARN_UNKNOWN.
+ | ?WARN_UNDEFINED_CALLBACK | ?WARN_UNKNOWN
+ | ?WARN_MAP_CONSTRUCTION.
%%
%% This is the representation of each warning as they will be returned
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index e03e4d5bb4..116260778c 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -210,10 +210,10 @@ check_contract(Contract, SuccType) ->
check_contract(#contract{contracts = Contracts}, SuccType, Opaques) ->
try
- Contracts1 = [{Contract, insert_constraints(Constraints, dict:new())}
+ Contracts1 = [{Contract, insert_constraints(Constraints)}
|| {Contract, Constraints} <- Contracts],
- Contracts2 = [erl_types:t_subst(Contract, Dict)
- || {Contract, Dict} <- Contracts1],
+ Contracts2 = [erl_types:t_subst(Contract, Map)
+ || {Contract, Map} <- Contracts1],
GenDomains = [erl_types:t_fun_args(C) || C <- Contracts2],
case check_domains(GenDomains) of
error ->
@@ -277,28 +277,45 @@ check_extraneous_1(Contract, SuccType) ->
case [CR || CR <- CRngs,
erl_types:t_is_none(erl_types:t_inf(CR, STRng))] of
[] ->
- CRngList = list_part(CRng),
- STRngList = list_part(STRng),
- case is_not_nil_list(CRngList) andalso is_not_nil_list(STRngList) of
- false -> ok;
- true ->
- CRngElements = erl_types:t_list_elements(CRngList),
- STRngElements = erl_types:t_list_elements(STRngList),
- Inf = erl_types:t_inf(CRngElements, STRngElements),
- case erl_types:t_is_none(Inf) of
- true -> {error, invalid_contract};
- false -> ok
- end
+ case bad_extraneous_list(CRng, STRng)
+ orelse bad_extraneous_map(CRng, STRng)
+ of
+ true -> {error, invalid_contract};
+ false -> ok
end;
CRs -> {error, {extra_range, erl_types:t_sup(CRs), STRng}}
end.
+bad_extraneous_list(CRng, STRng) ->
+ CRngList = list_part(CRng),
+ STRngList = list_part(STRng),
+ case is_not_nil_list(CRngList) andalso is_not_nil_list(STRngList) of
+ false -> false;
+ true ->
+ CRngElements = erl_types:t_list_elements(CRngList),
+ STRngElements = erl_types:t_list_elements(STRngList),
+ Inf = erl_types:t_inf(CRngElements, STRngElements),
+ erl_types:t_is_none(Inf)
+ end.
+
list_part(Type) ->
erl_types:t_inf(erl_types:t_list(), Type).
is_not_nil_list(Type) ->
erl_types:t_is_list(Type) andalso not erl_types:t_is_nil(Type).
+bad_extraneous_map(CRng, STRng) ->
+ CRngMap = map_part(CRng),
+ STRngMap = map_part(STRng),
+ (not is_empty_map(CRngMap)) andalso (not is_empty_map(STRngMap))
+ andalso is_empty_map(erl_types:t_inf(CRngMap, STRngMap)).
+
+map_part(Type) ->
+ erl_types:t_inf(erl_types:t_map(), Type).
+
+is_empty_map(Type) ->
+ erl_types:t_is_equal(Type, erl_types:t_from_term(#{})).
+
%% This is the heart of the "range function"
-spec process_contracts([contract_pair()], [erl_types:erl_type()]) ->
erl_types:erl_type().
@@ -327,15 +344,15 @@ process_contract({Contract, Constraints}, CallTypes0) ->
[erl_types:t_to_string(ContArgsFun),
erl_types:t_to_string(CallTypesFun)]),
case solve_constraints(ContArgsFun, CallTypesFun, Constraints) of
- {ok, VarDict} ->
- {ok, erl_types:t_subst(erl_types:t_fun_range(Contract), VarDict)};
+ {ok, VarMap} ->
+ {ok, erl_types:t_subst(erl_types:t_fun_range(Contract), VarMap)};
error -> error
end.
solve_constraints(Contract, Call, Constraints) ->
%% First make sure the call follows the constraints
- CDict = insert_constraints(Constraints, dict:new()),
- Contract1 = erl_types:t_subst(Contract, CDict),
+ CMap = insert_constraints(Constraints),
+ Contract1 = erl_types:t_subst(Contract, CMap),
%% Just a safe over-approximation.
%% TODO: Find the types for type variables properly
ContrArgs = erl_types:t_fun_args(Contract1),
@@ -343,7 +360,7 @@ solve_constraints(Contract, Call, Constraints) ->
InfList = erl_types:t_inf_lists(ContrArgs, CallArgs),
case erl_types:any_none_or_unit(InfList) of
true -> error;
- false -> {ok, CDict}
+ false -> {ok, CMap}
end.
%%Inf = erl_types:t_inf(Contract1, Call),
%% Then unify with the constrained call type.
@@ -373,23 +390,26 @@ warn_spec_missing_fun({M, F, A} = MFA, Contracts) ->
{?WARN_CONTRACT_SYNTAX, WarningInfo, {spec_missing_fun, [M, F, A]}}.
%% This treats the "when" constraints. It will be extended, we hope.
-insert_constraints([{subtype, Type1, Type2}|Left], Dict) ->
+insert_constraints(Constraints) ->
+ insert_constraints(Constraints, maps:new()).
+
+insert_constraints([{subtype, Type1, Type2}|Left], Map) ->
case erl_types:t_is_var(Type1) of
true ->
Name = erl_types:t_var_name(Type1),
- Dict1 = case dict:find(Name, Dict) of
- error ->
- dict:store(Name, Type2, Dict);
- {ok, VarType} ->
- dict:store(Name, erl_types:t_inf(VarType, Type2), Dict)
- end,
- insert_constraints(Left, Dict1);
+ Map1 = case maps:find(Name, Map) of
+ error ->
+ maps:put(Name, Type2, Map);
+ {ok, VarType} ->
+ maps:put(Name, erl_types:t_inf(VarType, Type2), Map)
+ end,
+ insert_constraints(Left, Map1);
false ->
%% A lot of things should change to add supertypes
throw({error, io_lib:format("First argument of is_subtype constraint "
"must be a type variable: ~p\n", [Type1])})
end;
-insert_constraints([], Dict) -> Dict.
+insert_constraints([], Map) -> Map.
-type types() :: erl_types:type_table().
@@ -459,7 +479,7 @@ initialize_constraints([], _MFA, _RecDict, _ExpTypes, _AllRecords, Acc) ->
initialize_constraints([Constr|Rest], MFA, RecDict, ExpTypes, AllRecords, Acc) ->
case Constr of
{type, _, constraint, [{atom, _, is_subtype}, [Type1, Type2]]} ->
- T1 = final_form(Type1, ExpTypes, MFA, AllRecords, dict:new()),
+ T1 = final_form(Type1, ExpTypes, MFA, AllRecords, maps:new()),
Entry = {T1, Type2},
initialize_constraints(Rest, MFA, RecDict, ExpTypes, AllRecords, [Entry|Acc]);
{type, _, constraint, [{atom,_,Name}, List]} ->
@@ -470,7 +490,7 @@ initialize_constraints([Constr|Rest], MFA, RecDict, ExpTypes, AllRecords, Acc) -
constraints_fixpoint(Constrs, MFA, RecDict, ExpTypes, AllRecords) ->
VarDict =
- constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, AllRecords, dict:new()),
+ constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, AllRecords, maps:new()),
constraints_fixpoint(VarDict, MFA, Constrs, RecDict, ExpTypes, AllRecords).
constraints_fixpoint(OldVarDict, MFA, Constrs, RecDict, ExpTypes, AllRecords) ->
@@ -482,7 +502,7 @@ constraints_fixpoint(OldVarDict, MFA, Constrs, RecDict, ExpTypes, AllRecords) ->
fun(Key, Value, Acc) ->
[{subtype, erl_types:t_var(Key), Value}|Acc]
end,
- FinalConstrs = dict:fold(DictFold, [], NewVarDict),
+ FinalConstrs = maps:fold(DictFold, [], NewVarDict),
{FinalConstrs, NewVarDict};
_Other ->
constraints_fixpoint(NewVarDict, MFA, Constrs, RecDict, ExpTypes, AllRecords)
@@ -492,7 +512,7 @@ final_form(Form, ExpTypes, MFA, AllRecords, VarDict) ->
from_form_with_check(Form, ExpTypes, MFA, AllRecords, VarDict).
from_form_with_check(Form, ExpTypes, MFA, AllRecords) ->
- from_form_with_check(Form, ExpTypes, MFA, AllRecords, dict:new()).
+ from_form_with_check(Form, ExpTypes, MFA, AllRecords, maps:new()).
from_form_with_check(Form, ExpTypes, MFA, AllRecords, VarDict) ->
Site = {spec, MFA},
@@ -503,7 +523,7 @@ from_form_with_check(Form, ExpTypes, MFA, AllRecords, VarDict) ->
constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, AllRecords, VarDict) ->
Subtypes =
constraints_to_subs(Constrs, MFA, RecDict, ExpTypes, AllRecords, VarDict, []),
- insert_constraints(Subtypes, dict:new()).
+ insert_constraints(Subtypes).
constraints_to_subs([], _MFA, _RecDict, _ExpTypes, _AllRecords, _VarDict, Acc) ->
Acc;
@@ -588,8 +608,8 @@ general_domain(List) ->
general_domain(List, erl_types:t_none()).
general_domain([{Sig, Constraints}|Left], AccSig) ->
- Dict = insert_constraints(Constraints, dict:new()),
- Sig1 = erl_types:t_subst(Sig, Dict),
+ Map = insert_constraints(Constraints),
+ Sig1 = erl_types:t_subst(Sig, Map),
general_domain(Left, erl_types:t_sup(AccSig, Sig1));
general_domain([], AccSig) ->
%% Get rid of all variables in the domain.
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 6e49043551..5ab0c39c04 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -43,7 +43,6 @@
-include("dialyzer.hrl").
-%%-import(helper, %% 'helper' could be any module doing sanity checks...
-import(erl_types,
[t_inf/2, t_inf/3, t_inf_lists/2, t_inf_lists/3,
t_inf_lists/3, t_is_equal/2, t_is_subtype/2, t_subtract/2,
@@ -72,7 +71,7 @@
t_tuple/0, t_tuple/1, t_tuple_args/1, t_tuple_args/2,
t_tuple_subtypes/2,
t_unit/0, t_unopaque/2,
- t_map/1
+ t_map/0, t_map/1, t_is_singleton/2
]).
%%-define(DEBUG, true).
@@ -126,8 +125,8 @@
curr_fun :: curr_fun()
}).
--record(map, {dict = dict:new() :: type_tab(),
- subst = dict:new() :: subst_tab(),
+-record(map, {map = maps:new() :: type_tab(),
+ subst = maps:new() :: subst_tab(),
modified = [] :: [Key :: term()],
modified_stack = [] :: [{[Key :: term()],reference()}],
ref = undefined :: reference() | undefined}).
@@ -135,10 +134,10 @@
-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()).
+ {'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()).
+-type type_tab() :: #{key() => type()}.
+-type subst_tab() :: #{key() => cerl:cerl()}.
%% Exported Types
@@ -342,8 +341,6 @@ traverse(Tree, Map, State) ->
handle_tuple(Tree, Map, State);
map ->
handle_map(Tree, Map, State);
- map_pair ->
- handle_map_pair(Tree, Map, State);
values ->
Elements = cerl:values_es(Tree),
{State1, Map1, EsType} = traverse_list(Elements, Map, State),
@@ -1102,15 +1099,54 @@ handle_try(Tree, Map, State) ->
%%----------------------------------------
handle_map(Tree,Map,State) ->
- Pairs = cerl:map_es(Tree),
- {State1, Map1, TypePairs} = traverse_list(Pairs,Map,State),
- {State1, Map1, t_map(TypePairs)}.
+ Pairs = cerl:map_es(Tree),
+ Arg = cerl:map_arg(Tree),
+ {State1, Map1, ArgType} = traverse(Arg, Map, State),
+ ArgType1 = t_inf(t_map(), ArgType),
+ case t_is_none_or_unit(ArgType1) of
+ true ->
+ {State1, Map1, ArgType1};
+ false ->
+ {State2, Map2, TypePairs, ExactKeys} =
+ traverse_map_pairs(Pairs, Map1, State1, t_none(), [], []),
+ InsertPair = fun({KV,assoc,_},Acc) -> erl_types:t_map_put(KV,Acc);
+ ({KV,exact,KVTree},Acc) ->
+ case t_is_none(T=erl_types:t_map_update(KV,Acc)) of
+ true -> throw({none, Acc, KV, KVTree});
+ false -> T
+ end
+ end,
+ try lists:foldl(InsertPair, ArgType1, TypePairs)
+ of ResT ->
+ BindT = t_map([{K, t_any()} || K <- ExactKeys]),
+ case bind_pat_vars_reverse([Arg], [BindT], [], Map2, State2) of
+ {error, _, _, _, _} -> {State2, Map2, ResT};
+ {Map3, _} -> {State2, Map3, ResT}
+ end
+ catch {none, MapType, {K,_}, KVTree} ->
+ Msg2 = {map_update, [format_type(MapType, State2),
+ format_type(K, State2)]},
+ {state__add_warning(State2, ?WARN_MAP_CONSTRUCTION, KVTree, Msg2),
+ Map2, t_none()}
+ end
+ end.
-handle_map_pair(Tree,Map,State) ->
- Key = cerl:map_pair_key(Tree),
- Val = cerl:map_pair_val(Tree),
+traverse_map_pairs([], Map, State, _ShadowKeys, PairAcc, KeyAcc) ->
+ {State, Map, lists:reverse(PairAcc), KeyAcc};
+traverse_map_pairs([Pair|Pairs], Map, State, ShadowKeys, PairAcc, KeyAcc) ->
+ Key = cerl:map_pair_key(Pair),
+ Val = cerl:map_pair_val(Pair),
+ Op = cerl:map_pair_op(Pair),
{State1, Map1, [K,V]} = traverse_list([Key,Val],Map,State),
- {State1, Map1, {K,V}}.
+ KeyAcc1 =
+ case cerl:is_literal(Op) andalso cerl:concrete(Op) =:= exact andalso
+ t_is_singleton(K, State#state.opaques) andalso
+ t_is_none(t_inf(ShadowKeys, K)) of
+ true -> [K|KeyAcc];
+ false -> KeyAcc
+ end,
+ traverse_map_pairs(Pairs, Map1, State1, t_sup(K, ShadowKeys),
+ [{{K,V},cerl:concrete(Op),Pair}|PairAcc], KeyAcc1).
%%----------------------------------------
@@ -1445,7 +1481,9 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
{NewMap, TypeOut} =
case cerl:type(Pat) of
alias ->
- AliasPat = cerl:alias_pat(Pat),
+ %% Map patterns are more allowing than the type of their literal. We
+ %% must unfold AliasPat if it is a literal.
+ AliasPat = dialyzer_utils:refold_pattern(cerl:alias_pat(Pat)),
Var = cerl:alias_var(Pat),
Map1 = enter_subst(Var, AliasPat, Map),
{Map2, [PatType]} = bind_pat_vars([AliasPat], [Type], [],
@@ -1486,14 +1524,59 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
{Map1, t_cons(HdType, TlType)}
end;
literal ->
- Literal = literal_type(Pat),
- case t_is_none(t_inf(Literal, Type, Opaques)) of
+ Pat0 = dialyzer_utils:refold_pattern(Pat),
+ case cerl:is_literal(Pat0) of
true ->
- bind_opaque_pats(Literal, Type, Pat, State);
- false -> {Map, Literal}
+ Literal = literal_type(Pat),
+ case t_is_none(t_inf(Literal, Type, Opaques)) of
+ true ->
+ bind_opaque_pats(Literal, Type, Pat, State);
+ false -> {Map, Literal}
+ end;
+ false ->
+ %% Retry with the unfolded pattern
+ {Map1, [PatType]}
+ = bind_pat_vars([Pat0], [Type], [], Map, State, Rev),
+ {Map1, PatType}
end;
map ->
- {Map, t_map([])};
+ MapT = t_inf(Type, t_map(), Opaques),
+ case t_is_none(MapT) of
+ true ->
+ bind_opaque_pats(t_map(), Type, Pat, State);
+ false ->
+ case Rev of
+ %% TODO: Reverse matching (propagating a matched subset back to a value)
+ true -> {Map, MapT};
+ false ->
+ FoldFun =
+ fun(Pair, {MapAcc, ListAcc}) ->
+ %% Only exact (:=) can appear in patterns
+ exact = cerl:concrete(cerl:map_pair_op(Pair)),
+ Key = cerl:map_pair_key(Pair),
+ KeyType =
+ case cerl:type(Key) of
+ var ->
+ case state__lookup_type_for_letrec(Key, State) of
+ error -> lookup_type(Key, MapAcc);
+ {ok, RecType} -> RecType
+ end;
+ literal ->
+ literal_type(Key)
+ end,
+ Bind = erl_types:t_map_get(KeyType, MapT),
+ {MapAcc1, [ValType]} =
+ bind_pat_vars([cerl:map_pair_val(Pair)],
+ [Bind], [], MapAcc, State, Rev),
+ case t_is_singleton(KeyType, Opaques) of
+ true -> {MapAcc1, [{KeyType, ValType}|ListAcc]};
+ false -> {MapAcc1, ListAcc}
+ end
+ end,
+ {Map1, Pairs} = lists:foldl(FoldFun, {Map, []}, cerl:map_es(Pat)),
+ {Map1, t_inf(MapT, t_map(Pairs))}
+ end
+ end;
tuple ->
Es = cerl:tuple_es(Pat),
{TypedRecord, Prototype} =
@@ -1682,7 +1765,7 @@ bind_opaque_pats(GenType, Type, Pat, State) ->
%%
bind_guard(Guard, Map, State) ->
- try bind_guard(Guard, Map, dict:new(), pos, State) of
+ try bind_guard(Guard, Map, maps:new(), pos, State) of
{Map1, _Type} -> Map1
catch
throw:{fail, Warning} -> {error, Warning};
@@ -1710,20 +1793,63 @@ bind_guard(Guard, Map, Env, Eval, State) ->
'try' ->
Arg = cerl:try_arg(Guard),
[Var] = cerl:try_vars(Guard),
+ EVars = cerl:try_evars(Guard),
%%?debug("Storing: ~w\n", [Var]),
- NewEnv = dict:store(get_label(Var), Arg, Env),
- bind_guard(cerl:try_body(Guard), Map, NewEnv, Eval, State);
+ Map1 = join_maps_begin(Map),
+ Map2 = mark_as_fresh(EVars, Map1),
+ %% Visit handler first so we know if it should be ignored
+ {{HandlerMap, HandlerType}, HandlerE} =
+ try {bind_guard(cerl:try_handler(Guard), Map2, Env, Eval, State), none}
+ catch throw:HE ->
+ {{Map2, t_none()}, HE}
+ end,
+ BodyEnv = maps:put(get_label(Var), Arg, Env),
+ Wanted = case Eval of pos -> t_atom(true); neg -> t_atom(false);
+ dont_know -> t_any() end,
+ case t_is_none(t_inf(HandlerType, Wanted)) of
+ %% Handler won't save us; pretend it does not exist
+ true -> bind_guard(cerl:try_body(Guard), Map, BodyEnv, Eval, State);
+ false ->
+ {{BodyMap, BodyType}, BodyE} =
+ try {bind_guard(cerl:try_body(Guard), Map1, BodyEnv,
+ Eval, State), none}
+ catch throw:BE ->
+ {{Map1, t_none()}, BE}
+ end,
+ Map3 = join_maps_end([BodyMap, HandlerMap], Map1),
+ case t_is_none(Sup = t_sup(BodyType, HandlerType)) of
+ true ->
+ %% Pick a reason. N.B. We assume that the handler is always
+ %% compiler-generated if the body is; that way, we won't need to
+ %% check.
+ Fatality = case {BodyE, HandlerE} of
+ {{fatal_fail, _}, _} -> fatal_fail;
+ {_, {fatal_fail, _}} -> fatal_fail;
+ _ -> fail
+ end,
+ throw({Fatality,
+ case {BodyE, HandlerE} of
+ {{_, Rsn}, _} when Rsn =/= none -> Rsn;
+ {_, {_,Rsn}} -> Rsn;
+ _ -> none
+ end});
+ false -> {Map3, Sup}
+ end
+ end;
tuple ->
Es0 = cerl:tuple_es(Guard),
{Map1, Es} = bind_guard_list(Es0, Map, Env, dont_know, State),
{Map1, t_tuple(Es)};
map ->
- {Map, t_map([])};
+ case Eval of
+ dont_know -> handle_guard_map(Guard, Map, Env, State);
+ _PosOrNeg -> {Map, t_none()} %% Map exprs do not produce bools
+ end;
'let' ->
Arg = cerl:let_arg(Guard),
[Var] = cerl:let_vars(Guard),
%%?debug("Storing: ~w\n", [Var]),
- NewEnv = dict:store(get_label(Var), Arg, Env),
+ NewEnv = maps:put(get_label(Var), Arg, Env),
bind_guard(cerl:let_body(Guard), Map, NewEnv, Eval, State);
values ->
Es = cerl:values_es(Guard),
@@ -1732,7 +1858,7 @@ bind_guard(Guard, Map, Env, Eval, State) ->
{Map, Type};
var ->
?debug("Looking for var(~w)...", [cerl_trees:get_label(Guard)]),
- case dict:find(get_label(Guard), Env) of
+ case maps:find(get_label(Guard), Env) of
error ->
?debug("Did not find it\n", []),
Type = lookup_type(Guard, Map),
@@ -1761,7 +1887,7 @@ handle_guard_call(Guard, Map, Env, Eval, State) ->
{erlang, F, 1} when F =:= is_atom; F =:= is_boolean;
F =:= is_binary; F =:= is_bitstring;
F =:= is_float; F =:= is_function;
- F =:= is_integer; F =:= is_list;
+ F =:= is_integer; F =:= is_list; F =:= is_map;
F =:= is_number; F =:= is_pid; F =:= is_port;
F =:= is_reference; F =:= is_tuple ->
handle_guard_type_test(Guard, F, Map, Env, Eval, State);
@@ -1841,6 +1967,7 @@ bind_type_test(Eval, TypeTest, ArgType, State) ->
is_function -> t_fun();
is_integer -> t_integer();
is_list -> t_maybe_improper_list();
+ is_map -> t_map();
is_number -> t_number();
is_pid -> t_pid();
is_port -> t_port();
@@ -2349,6 +2476,30 @@ bind_guard_list([G|Gs], Map, Env, Eval, State, Acc) ->
bind_guard_list([], Map, _Env, _Eval, _State, Acc) ->
{Map, lists:reverse(Acc)}.
+handle_guard_map(Guard, Map, Env, State) ->
+ Pairs = cerl:map_es(Guard),
+ Arg = cerl:map_arg(Guard),
+ {Map1, ArgType0} = bind_guard(Arg, Map, Env, dont_know, State),
+ ArgType1 = t_inf(t_map(), ArgType0),
+ case t_is_none_or_unit(ArgType1) of
+ true -> {Map1, t_none()};
+ false ->
+ {Map2, TypePairs} = bind_guard_map_pairs(Pairs, Map1, Env, State, []),
+ {Map2, lists:foldl(fun({KV,assoc},Acc) -> erl_types:t_map_put(KV,Acc);
+ ({KV,exact},Acc) -> erl_types:t_map_update(KV,Acc)
+ end, ArgType1, TypePairs)}
+ end.
+
+bind_guard_map_pairs([], Map, _Env, _State, PairAcc) ->
+ {Map, lists:reverse(PairAcc)};
+bind_guard_map_pairs([Pair|Pairs], Map, Env, State, PairAcc) ->
+ Key = cerl:map_pair_key(Pair),
+ Val = cerl:map_pair_val(Pair),
+ Op = cerl:map_pair_op(Pair),
+ {Map1, [K,V]} = bind_guard_list([Key,Val],Map,Env,dont_know,State),
+ bind_guard_map_pairs(Pairs, Map1, Env, State,
+ [{{K,V},cerl:concrete(Op)}|PairAcc]).
+
-type eval() :: 'pos' | 'neg' | 'dont_know'.
-spec signal_guard_fail(eval(), cerl:c_call(), [type()],
@@ -2421,7 +2572,9 @@ filter_fail_clauses([Clause|Left]) ->
case (cerl:clause_pats(Clause) =:= []) of
true ->
Body = cerl:clause_body(Clause),
- case cerl:is_literal(Body) andalso (cerl:concrete(Body) =:= fail) of
+ case cerl:is_literal(Body) andalso (cerl:concrete(Body) =:= fail) orelse
+ cerl:is_c_primop(Body) andalso
+ (cerl:atom_val(cerl:primop_name(Body)) =:= match_fail) of
true -> filter_fail_clauses(Left);
false -> [Clause|filter_fail_clauses(Left)]
end;
@@ -2535,10 +2688,10 @@ join_maps_end(Maps, MapOut) ->
#map{ref = Ref, modified_stack = [{M1,R1} | S]} = MapOut,
true = lists:all(fun(M) -> M#map.ref =:= Ref end, Maps), % sanity
Keys0 = lists:usort(lists:append([M#map.modified || M <- Maps])),
- #map{dict = Dict, subst = Subst} = MapOut,
+ #map{map = Map, subst = Subst} = MapOut,
Keys = [Key ||
Key <- Keys0,
- dict:is_key(Key, Dict) orelse dict:is_key(Key, Subst)],
+ maps:is_key(Key, Map) orelse maps:is_key(Key, Subst)],
Out = case Maps of
[] -> join_maps(Maps, MapOut);
_ -> join_maps(Keys, Maps, MapOut)
@@ -2549,8 +2702,8 @@ join_maps_end(Maps, MapOut) ->
modified_stack = S}.
join_maps(Maps, MapOut) ->
- #map{dict = Dict, subst = Subst} = MapOut,
- Keys = ordsets:from_list(dict:fetch_keys(Dict) ++ dict:fetch_keys(Subst)),
+ #map{map = Map, subst = Subst} = MapOut,
+ Keys = ordsets:from_list(maps:keys(Map) ++ maps:keys(Subst)),
join_maps(Keys, Maps, MapOut).
join_maps(Keys, Maps, MapOut) ->
@@ -2579,11 +2732,11 @@ join_maps_one_key([], _Key, AccType) ->
-ifdef(DEBUG).
debug_join_check(Maps, MapOut, Out) ->
- #map{dict = Dict, subst = Subst} = Out,
- #map{dict = Dict2, subst = Subst2} = join_maps(Maps, MapOut),
- F = fun(D) -> lists:keysort(1, dict:to_list(D)) end,
+ #map{map = Map, subst = Subst} = Out,
+ #map{map = Map2, subst = Subst2} = join_maps(Maps, MapOut),
+ F = fun(D) -> lists:keysort(1, maps:to_list(D)) end,
[throw({bug, join_maps}) ||
- F(Dict) =/= F(Dict2) orelse F(Subst) =/= F(Subst2)].
+ F(Map) =/= F(Map2) orelse F(Subst) =/= F(Subst2)].
-else.
debug_join_check(_Maps, _MapOut, _Out) -> ok.
-endif.
@@ -2614,15 +2767,15 @@ enter_type(Key, Val, MS) ->
enter_type_lists(Keys, t_to_tlist(Val), MS)
end;
false ->
- #map{dict = Dict, subst = Subst} = MS,
+ #map{map = Map, subst = Subst} = MS,
KeyLabel = get_label(Key),
- case dict:find(KeyLabel, Subst) of
+ case maps:find(KeyLabel, Subst) of
{ok, NewKey} ->
?debug("Binding ~p to ~p\n", [KeyLabel, NewKey]),
enter_type(NewKey, Val, MS);
error ->
?debug("Entering ~p :: ~s\n", [KeyLabel, t_to_string(Val)]),
- case dict:find(KeyLabel, Dict) of
+ case maps:find(KeyLabel, Map) of
{ok, Value} ->
case erl_types:t_is_equal(Val, Value) of
true -> MS;
@@ -2634,13 +2787,14 @@ enter_type(Key, Val, MS) ->
end
end.
-store_map(Key, Val, #map{dict = Dict, ref = undefined} = Map) ->
- Map#map{dict = dict:store(Key, Val, Dict)};
-store_map(Key, Val, #map{dict = Dict, modified = Mod} = Map) ->
- Map#map{dict = dict:store(Key, Val, Dict), modified = [Key | Mod]}.
+store_map(Key, Val, #map{map = Map, ref = undefined} = MapRec) ->
+ MapRec#map{map = maps:put(Key, Val, Map)};
+store_map(Key, Val, #map{map = Map, modified = Mod} = MapRec) ->
+ MapRec#map{map = maps:put(Key, Val, Map), modified = [Key | Mod]}.
-enter_subst(Key, Val, #map{subst = Subst} = MS) ->
+enter_subst(Key, Val0, #map{subst = Subst} = MS) ->
KeyLabel = get_label(Key),
+ Val = dialyzer_utils:refold_pattern(Val0),
case cerl:is_literal(Val) of
true ->
store_map(KeyLabel, literal_type(Val), MS);
@@ -2649,7 +2803,7 @@ enter_subst(Key, Val, #map{subst = Subst} = MS) ->
false -> MS;
true ->
ValLabel = get_label(Val),
- case dict:find(ValLabel, Subst) of
+ case maps:find(ValLabel, Subst) of
{ok, NewVal} ->
enter_subst(Key, NewVal, MS);
error ->
@@ -2663,22 +2817,22 @@ enter_subst(Key, Val, #map{subst = Subst} = MS) ->
end.
store_subst(Key, Val, #map{subst = S, ref = undefined} = Map) ->
- Map#map{subst = dict:store(Key, Val, S)};
+ Map#map{subst = maps:put(Key, Val, S)};
store_subst(Key, Val, #map{subst = S, modified = Mod} = Map) ->
- Map#map{subst = dict:store(Key, Val, S), modified = [Key | Mod]}.
+ Map#map{subst = maps:put(Key, Val, S), modified = [Key | Mod]}.
-lookup_type(Key, #map{dict = Dict, subst = Subst}) ->
- lookup(Key, Dict, Subst, t_none()).
+lookup_type(Key, #map{map = Map, subst = Subst}) ->
+ lookup(Key, Map, Subst, t_none()).
-lookup(Key, Dict, Subst, AnyNone) ->
+lookup(Key, Map, Subst, AnyNone) ->
case cerl:is_literal(Key) of
true -> literal_type(Key);
false ->
Label = get_label(Key),
- case dict:find(Label, Subst) of
- {ok, NewKey} -> lookup(NewKey, Dict, Subst, AnyNone);
+ case maps:find(Label, Subst) of
+ {ok, NewKey} -> lookup(NewKey, Map, Subst, AnyNone);
error ->
- case dict:find(Label, Dict) of
+ case maps:find(Label, Map) of
{ok, Val} -> Val;
error -> AnyNone
end
@@ -2703,6 +2857,9 @@ mark_as_fresh([Tree|Left], Map) ->
bitstr ->
%% The Size field is not fresh.
{SubTrees1 -- [cerl:bitstr_size(Tree)], Map};
+ map_pair ->
+ %% The keys are not fresh
+ {SubTrees1 -- [cerl:map_pair_key(Tree)], Map};
var ->
{SubTrees1, enter_type(Tree, t_any(), Map)};
_ ->
@@ -2713,12 +2870,12 @@ mark_as_fresh([], Map) ->
Map.
-ifdef(DEBUG).
-debug_pp_map(#map{dict = Dict}=Map) ->
- Keys = dict:fetch_keys(Dict),
+debug_pp_map(#map{map = Map}=MapRec) ->
+ Keys = maps:keys(Map),
io:format("Map:\n", []),
lists:foreach(fun (Key) ->
io:format("\t~w :: ~s\n",
- [Key, t_to_string(lookup_type(Key, Map))])
+ [Key, t_to_string(lookup_type(Key, MapRec))])
end, Keys),
ok.
-else.
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 9f344d87ff..30d2bdeca4 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -422,7 +422,7 @@ gui_loop(#gui_state{backend_pid = BackendPid, doc_plt = DocPlt,
#wx{id=?menuID_HELP_ABOUT, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
Message = " This is DIALYZER version " ++ ?VSN ++ " \n"++
- "DIALYZER is a DIscrepany AnaLYZer for ERlang programs.\n\n"++
+ "DIALYZER is a DIscrepancy AnaLYZer for ERlang programs.\n\n"++
" Copyright (C) Tobias Lindahl <[email protected]>\n"++
" Kostis Sagonas <[email protected]>\n\n",
output_sms(State, "About Dialyzer", Message, info),
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index dd81dd01ed..add660eae9 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -47,6 +47,7 @@ build(Opts) ->
?WARN_CALLGRAPH,
?WARN_FAILING_CALL,
?WARN_BIN_CONSTRUCTION,
+ ?WARN_MAP_CONSTRUCTION,
?WARN_CONTRACT_RANGE,
?WARN_CONTRACT_TYPES,
?WARN_CONTRACT_SYNTAX,
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 5f0881bbcd..1787b66192 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -48,6 +48,7 @@
t_is_float/1, t_is_fun/1,
t_is_integer/1, t_non_neg_integer/0,
t_is_list/1, t_is_nil/1, t_is_none/1, t_is_number/1,
+ t_is_singleton/1,
t_limit/2, t_list/0, t_list/1,
t_list_elements/1, t_nonempty_list/1, t_maybe_improper_list/0,
@@ -57,7 +58,7 @@
t_timeout/0, t_tuple/0, t_tuple/1,
t_var/1, t_var_name/1,
t_none/0, t_unit/0,
- t_map/1
+ t_map/0, t_map/1, t_map_get/2, t_map_put/2
]).
-include("dialyzer.hrl").
@@ -65,9 +66,11 @@
%%-----------------------------------------------------------------------------
-type dep() :: integer(). %% type variable names used as constraint ids
+-type deps() :: ordsets:ordset(dep()).
+
-type type_var() :: erl_types:erl_type(). %% actually: {'c','var',_,_}
--record(fun_var, {'fun' :: fun((_) -> erl_types:erl_type()), deps :: [dep()],
+-record(fun_var, {'fun' :: fun((_) -> erl_types:erl_type()), deps :: deps(),
origin :: integer() | 'undefined'}).
-type constr_op() :: 'eq' | 'sub'.
@@ -76,20 +79,21 @@
-record(constraint, {lhs :: erl_types:erl_type(),
op :: constr_op(),
rhs :: fvar_or_type(),
- deps :: [dep()]}).
+ deps :: deps()}).
-type constraint() :: #constraint{}.
+-type mask() :: ordsets:ordset(non_neg_integer()).
+
-record(constraint_list, {type :: 'conj' | 'disj',
list :: [constr()],
- deps :: [dep()],
- masks = [] :: [{dep(),[non_neg_integer()]}] |
- {'d',dict:dict(dep(), [non_neg_integer()])},
+ deps :: deps(),
+ masks = maps:new() :: #{dep() => mask()},
id :: {'list', dep()} | 'undefined'}).
-type constraint_list() :: #constraint_list{}.
--record(constraint_ref, {id :: type_var(), deps :: [dep()]}).
+-record(constraint_ref, {id :: type_var(), deps :: deps()}).
-type constraint_ref() :: #constraint_ref{}.
@@ -98,34 +102,33 @@
-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 typesig_funmap() :: #{type_var() => type_var()}.
-type prop_types() :: dict:dict(label(), types()).
--type dict_or_ets() :: {'d', prop_types()} | {'e', ets:tid()}.
-
--record(state, {callgraph :: dialyzer_callgraph:callgraph()
- | 'undefined',
- cs = [] :: [constr()],
- cmap = {'d', dict:new()} :: dict_or_ets(),
- fun_map = [] :: typesig_funmap(),
- fun_arities = dict:new() :: dict:dict(type_var(), arity()),
- in_match = false :: boolean(),
- in_guard = false :: boolean(),
- module :: module(),
- 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()
- | 'undefined',
- prop_types = {'d', dict:new()} :: dict_or_ets(),
- records = dict:new() :: types(),
- scc = [] :: [type_var()],
- mfas :: [tuple()],
- solvers = [] :: [solver()]
+-record(state, {callgraph :: dialyzer_callgraph:callgraph()
+ | 'undefined',
+ cs = [] :: [constr()],
+ cmap = maps:new() :: #{type_var() => constr()},
+ fun_map = maps:new() :: typesig_funmap(),
+ fun_arities = maps:new() :: #{type_var() => arity()},
+ in_match = false :: boolean(),
+ in_guard = false :: boolean(),
+ module :: module(),
+ name_map = maps:new() :: #{mfa() => cerl:c_fun()},
+ next_label = 0 :: label(),
+ self_rec :: 'false' | erl_types:erl_type(),
+ plt :: dialyzer_plt:plt()
+ | 'undefined',
+ prop_types = dict:new() :: prop_types(),
+ records = dict:new() :: types(),
+ scc = [] :: ordsets:ordset(type_var()),
+ mfas :: [mfa()],
+ solvers = [] :: [solver()]
}).
+-type state() :: #state{}.
+
%%-----------------------------------------------------------------------------
-define(TYPE_LIMIT, 4).
@@ -187,7 +190,8 @@ analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) ->
Funs = state__scc(State3),
pp_constrs_scc(Funs, State3),
constraints_to_dot_scc(Funs, State3),
- solve(Funs, State3).
+ T = solve(Funs, State3),
+ dict:from_list(maps:to_list(T)).
assert_format_of_scc([{_MFA, {_Var, _Fun}, _Records}|Left]) ->
assert_format_of_scc(Left);
@@ -311,7 +315,7 @@ traverse(Tree, DefinedVars, State) ->
Hd = cerl:cons_hd(Tree),
Tl = cerl:cons_tl(Tree),
{State1, [HdVar, TlVar]} = traverse_list([Hd, Tl], DefinedVars, State),
- case cerl:is_literal(cerl:fold_literal(Tree)) of
+ case cerl:is_literal(fold_literal_maybe_match(Tree, State)) of
true ->
%% We do not need to do anything more here.
{State, t_cons(HdVar, TlVar)};
@@ -392,8 +396,18 @@ traverse(Tree, DefinedVars, State) ->
{State2, _} = traverse_list(Funs, DefinedVars1, State1),
traverse(Body, DefinedVars1, State2);
literal ->
- Type = t_from_term(cerl:concrete(Tree)),
- {State, Type};
+ %% Maps are special; a literal pattern matches more than just the value
+ %% constructed by the literal. For example #{} constructs the empty map,
+ %% but matches every map.
+ case state__is_in_match(State) of
+ true ->
+ Tree1 = dialyzer_utils:refold_pattern(Tree),
+ case cerl:is_literal(Tree1) of
+ false -> traverse(Tree1, DefinedVars, State);
+ true -> {State, t_from_term(cerl:concrete(Tree))}
+ end;
+ _ -> {State, t_from_term(cerl:concrete(Tree))}
+ end;
module ->
Defs = cerl:module_defs(Tree),
Funs = [Fun || {_Var, Fun} <- Defs],
@@ -437,7 +451,7 @@ traverse(Tree, DefinedVars, State) ->
Elements = cerl:tuple_es(Tree),
{State1, EVars} = traverse_list(Elements, DefinedVars, State),
{State2, TupleType} =
- case cerl:is_literal(cerl:fold_literal(Tree)) of
+ case cerl:is_literal(fold_literal_maybe_match(Tree, State1)) of
true ->
%% We do not need to do anything more here.
{State, t_tuple(EVars)};
@@ -476,7 +490,111 @@ traverse(Tree, DefinedVars, State) ->
[] -> {State2, TupleType}
end;
map ->
- {State, t_map([])};
+ Entries = cerl:map_es(Tree),
+ MapFoldFun = fun(Entry, AccState) ->
+ AccState1 = state__set_in_match(AccState, false),
+ {AccState2, KeyVar} = traverse(cerl:map_pair_key(Entry),
+ DefinedVars, AccState1),
+ AccState3 = state__set_in_match(
+ AccState2, state__is_in_match(AccState)),
+ {AccState4, ValVar} = traverse(cerl:map_pair_val(Entry),
+ DefinedVars, AccState3),
+ {{KeyVar, ValVar}, AccState4}
+ end,
+ {Pairs, State1} = lists:mapfoldl(MapFoldFun, State, Entries),
+ %% We mustn't recurse into map arguments to matches. Not only are they
+ %% syntactically only allowed to be the literal #{}, but that would also
+ %% cause an infinite recursion, since traverse/3 unfolds literals with
+ %% maps in them using dialyzer_utils:reflow_pattern/1.
+ {State2, ArgVar} =
+ case state__is_in_match(State) of
+ false -> traverse(cerl:map_arg(Tree), DefinedVars, State1);
+ true -> {State1, t_map()}
+ end,
+ MapVar = mk_var(Tree),
+ MapType = ?mk_fun_var(
+ fun(Map) ->
+ lists:foldl(
+ fun({K,V}, TypeAcc) ->
+ t_map_put({lookup_type(K, Map),
+ lookup_type(V, Map)},
+ TypeAcc)
+ end, t_inf(t_map(), lookup_type(ArgVar, Map)),
+ Pairs)
+ end, [ArgVar | lists:append([[K,V] || {K,V} <- Pairs])]),
+ %% TODO: does the "same element appearing several times" problem apply
+ %% here too?
+ Fun =
+ fun({KeyVar, ValVar}, {AccState, ShadowKeys}) ->
+ %% If Val is known to be the last association of Key (i.e. Key
+ %% is not in ShadowKeys), Val must be a subtype of what is
+ %% associated to Key in Tree
+ TypeFun =
+ fun(Map) ->
+ KeyType = lookup_type(KeyVar, Map),
+ case t_is_singleton(KeyType) of
+ false -> t_any();
+ true ->
+ MT = t_inf(lookup_type(MapVar, Map), t_map()),
+ case t_is_none(MT) of
+ true -> t_none();
+ false ->
+ DisjointFromKeyType =
+ fun(ShadowKey) ->
+ t_is_none(t_inf(lookup_type(ShadowKey, Map),
+ KeyType))
+ end,
+ case lists:all(DisjointFromKeyType, ShadowKeys) of
+ true -> t_map_get(KeyType, MT);
+ %% A later association might shadow this one
+ false -> t_any()
+ end
+ end
+ end
+ end,
+ ValType = ?mk_fun_var(TypeFun, [KeyVar, MapVar | ShadowKeys]),
+ {state__store_conj(ValVar, sub, ValType, AccState),
+ [KeyVar | ShadowKeys]}
+ end,
+ %% Accumulate shadowing keys right-to-left
+ {State3, _} = lists:foldr(Fun, {State2, []}, Pairs),
+ %% In a map expression, Arg must contain all keys that are inserted with
+ %% the exact (:=) operator, and are known (i.e. are not in ShadowedKeys)
+ %% to not have been introduced by a previous association
+ State4 =
+ case state__is_in_match(State) of
+ true -> State3;
+ false ->
+ ArgFun =
+ fun(Map) ->
+ FoldFun =
+ fun({{KeyVar, _}, Entry}, {AccType, ShadowedKeys}) ->
+ OpTree = cerl:map_pair_op(Entry),
+ KeyType = lookup_type(KeyVar, Map),
+ AccType1 =
+ case cerl:is_literal(OpTree) andalso
+ cerl:concrete(OpTree) =:= exact of
+ true ->
+ case t_is_none(t_inf(ShadowedKeys, KeyType)) of
+ true ->
+ t_map_put({KeyType, t_any()}, AccType);
+ false ->
+ AccType
+ end;
+ false ->
+ AccType
+ end,
+ {AccType1, t_sup(KeyType, ShadowedKeys)}
+ end,
+ %% Accumulate shadowed keys left-to-right
+ {ResType, _} = lists:foldl(FoldFun, {t_map(), t_none()},
+ lists:zip(Pairs, Entries)),
+ ResType
+ end,
+ ArgType = ?mk_fun_var(ArgFun, [KeyVar || {KeyVar, _} <- Pairs]),
+ state__store_conj(ArgVar, sub, ArgType, State3)
+ end,
+ {state__store_conj(MapVar, sub, MapType, State4), MapVar};
values ->
%% We can get into trouble when unifying products that have the
%% same element appearing several times. Handle these cases by
@@ -827,11 +945,11 @@ get_safe_underapprox(Pats, Guard) ->
Map1 = cerl_trees:fold(fun(X, Acc) ->
case cerl:is_c_var(X) of
true ->
- dict:store(cerl_trees:get_label(X), t_any(),
- Acc);
+ maps:put(cerl_trees:get_label(X), t_any(),
+ Acc);
false -> Acc
end
- end, dict:new(), cerl:c_values(Pats)),
+ end, maps:new(), cerl:c_values(Pats)),
{Type, Map2} = get_underapprox_from_guard(Guard, Map1),
Map3 = case t_is_none(t_inf(t_from_term(true), Type)) of
true -> throw(dont_know);
@@ -839,8 +957,8 @@ get_safe_underapprox(Pats, Guard) ->
case cerl:is_c_var(Guard) of
false -> Map2;
true ->
- dict:store(cerl_trees:get_label(Guard),
- t_from_term(true), Map2)
+ maps:put(cerl_trees:get_label(Guard),
+ t_from_term(true), Map2)
end
end,
{Ts, _Map4} = get_safe_underapprox_1(Pats, [], Map3),
@@ -866,7 +984,7 @@ get_underapprox_from_guard(Tree, Map) ->
case t_is_none(Inf) of
true -> throw(dont_know);
false ->
- {True, dict:store(cerl_trees:get_label(Fun), Inf, Map1)}
+ {True, maps:put(cerl_trees:get_label(Fun), Inf, Map1)}
end
end;
MFA ->
@@ -882,7 +1000,7 @@ get_underapprox_from_guard(Tree, Map) ->
case cerl:is_literal(Arg) of
true -> {True, Map1};
false ->
- {True, dict:store(cerl_trees:get_label(Arg), Inf, Map1)}
+ {True, maps:put(cerl_trees:get_label(Arg), Inf, Map1)}
end
end;
error ->
@@ -914,7 +1032,7 @@ get_underapprox_from_guard(Tree, Map) ->
end;
var ->
Type =
- case dict:find(cerl_trees:get_label(Tree), Map) of
+ case maps:find(cerl_trees:get_label(Tree), Map) of
error -> throw(dont_know);
{ok, T} -> T
end,
@@ -948,6 +1066,7 @@ get_type_test({erlang, is_float, 1}) -> {ok, t_float()};
get_type_test({erlang, is_function, 1}) -> {ok, t_fun()};
get_type_test({erlang, is_integer, 1}) -> {ok, t_integer()};
get_type_test({erlang, is_list, 1}) -> {ok, t_list()};
+get_type_test({erlang, is_map, 1}) -> {ok, t_map()};
get_type_test({erlang, is_number, 1}) -> {ok, t_number()};
get_type_test({erlang, is_pid, 1}) -> {ok, t_pid()};
get_type_test({erlang, is_port, 1}) -> {ok, t_port()};
@@ -1004,7 +1123,9 @@ bitstr_val_constr(SizeType, UnitVal, Flags) ->
end
end.
-get_safe_underapprox_1([Pat|Left], Acc, Map) ->
+get_safe_underapprox_1([Pat0|Left], Acc, Map) ->
+ %% Maps should be treated as patterns, not as literals
+ Pat = dialyzer_utils:refold_pattern(Pat0),
case cerl:type(Pat) of
alias ->
APat = cerl:alias_pat(Pat),
@@ -1015,7 +1136,7 @@ get_safe_underapprox_1([Pat|Left], Acc, Map) ->
case t_is_none(Inf) of
true -> throw(dont_know);
false ->
- Map3 = dict:store(cerl_trees:get_label(AVar), Inf, Map2),
+ Map3 = maps:put(cerl_trees:get_label(AVar), Inf, Map2),
get_safe_underapprox_1(Left, [Inf|Acc], Map3)
end;
binary ->
@@ -1048,15 +1169,42 @@ get_safe_underapprox_1([Pat|Left], Acc, Map) ->
Type = t_tuple(Ts),
get_safe_underapprox_1(Left, [Type|Acc], Map1);
map ->
- %% TODO: Can maybe do something here
- throw(dont_know);
+ %% Some assertions in case the syntax gets more premissive in the future
+ true = #{} =:= cerl:concrete(cerl:map_arg(Pat)),
+ true = lists:all(fun(P) ->
+ cerl:is_literal(Op = cerl:map_pair_op(P)) andalso
+ exact =:= cerl:concrete(Op)
+ end, cerl:map_es(Pat)),
+ KeyTrees = lists:map(fun cerl:map_pair_key/1, cerl:map_es(Pat)),
+ ValTrees = lists:map(fun cerl:map_pair_val/1, cerl:map_es(Pat)),
+ %% Keys must not be underapproximated. Overapproximations are safe.
+ Keys = get_safe_overapprox(KeyTrees),
+ {Vals, Map1} = get_safe_underapprox_1(ValTrees, [], Map),
+ case lists:all(fun erl_types:t_is_singleton/1, Keys) of
+ false -> throw(dont_know);
+ true -> ok
+ end,
+ SortedPairs = lists:sort(lists:zip(Keys, Vals)),
+ %% We need to deal with duplicates ourselves
+ SquashDuplicates =
+ fun SquashDuplicates([{K,First},{K,Second}|List]) ->
+ case t_is_none(Inf = t_inf(First, Second)) of
+ true -> throw(dont_know);
+ false -> [{K, Inf}|SquashDuplicates(List)]
+ end;
+ SquashDuplicates([Good|Rest]) ->
+ [Good|SquashDuplicates(Rest)];
+ SquashDuplicates([]) -> []
+ end,
+ Type = t_map(SquashDuplicates(SortedPairs)),
+ get_safe_underapprox_1(Left, [Type|Acc], Map1);
values ->
Es = cerl:values_es(Pat),
{Ts, Map1} = get_safe_underapprox_1(Es, [], Map),
Type = t_product(Ts),
get_safe_underapprox_1(Left, [Type|Acc], Map1);
var ->
- case dict:find(cerl_trees:get_label(Pat), Map) of
+ case maps:find(cerl_trees:get_label(Pat), Map) of
error -> throw(dont_know);
{ok, VarType} -> get_safe_underapprox_1(Left, [VarType|Acc], Map)
end
@@ -1064,6 +1212,15 @@ get_safe_underapprox_1([Pat|Left], Acc, Map) ->
get_safe_underapprox_1([], Acc, Map) ->
{lists:reverse(Acc), Map}.
+get_safe_overapprox(Pats) ->
+ lists:map(fun get_safe_overapprox_1/1, Pats).
+
+get_safe_overapprox_1(Pat) ->
+ case cerl:is_literal(Lit = cerl:fold_literal(Pat)) of
+ true -> t_from_term(cerl:concrete(Lit));
+ false -> t_any()
+ end.
+
%%----------------------------------------
%% Guards
%%
@@ -1263,6 +1420,8 @@ get_bif_constr({erlang, is_integer, 1}, Dst, [Arg], State) ->
get_bif_test_constr(Dst, Arg, t_integer(), State);
get_bif_constr({erlang, is_list, 1}, Dst, [Arg], State) ->
get_bif_test_constr(Dst, Arg, t_maybe_improper_list(), State);
+get_bif_constr({erlang, is_map, 1}, Dst, [Arg], State) ->
+ get_bif_test_constr(Dst, Arg, t_map(), State);
get_bif_constr({erlang, is_number, 1}, Dst, [Arg], State) ->
get_bif_test_constr(Dst, Arg, t_number(), State);
get_bif_constr({erlang, is_pid, 1}, Dst, [Arg], State) ->
@@ -1644,12 +1803,16 @@ solve([Fun], State) ->
solve([_|_] = SCC, State) ->
?debug("============ Analyzing SCC: ~w ===========\n",
[[debug_lookup_name(F) || F <- SCC]]),
- {Parallel, NewState} =
- case parallel_split(SCC) of
- false -> {false, State};
- SplitSCC -> {SplitSCC, minimize_state(State)}
- end,
- solve_scc(SCC, Parallel, map_new(), NewState, false).
+ Users = comp_users(SCC, State),
+ solve_scc(SCC, map_new(), State, Users, _ToSolve=SCC, false).
+
+comp_users(SCC, State) ->
+ Vars0 = [{Fun, state__get_rec_var(Fun, State)} || Fun <- SCC],
+ Vars = lists:sort([t_var_name(Var) || {_, {ok, Var}} <- Vars0]),
+ family([{t_var(V), F} ||
+ F <- SCC,
+ V <- ordsets:intersection(get_deps(state__get_cs(F, State)),
+ Vars)]).
solve_fun(Fun, FunMap, State) ->
Cs = state__get_cs(Fun, State),
@@ -1664,7 +1827,7 @@ solve_fun(Fun, FunMap, State) ->
end,
enter_type(Fun, NewType, NewFunMap1).
-solve_scc(SCC, Parallel, Map, State, TryingUnit) ->
+solve_scc(SCC, Map, State, Users, ToSolve, TryingUnit) ->
Vars0 = [{Fun, state__get_rec_var(Fun, State)} || Fun <- SCC],
Vars = [Var || {_, {ok, Var}} <- Vars0],
Funs = [Fun || {Fun, {ok, _}} <- Vars0],
@@ -1672,16 +1835,13 @@ solve_scc(SCC, Parallel, Map, State, TryingUnit) ->
RecTypes = [t_limit(Type, ?TYPE_LIMIT) || Type <- Types],
CleanMap = lists:foldl(fun(Fun, AccFunMap) ->
erase_type(t_var_name(Fun), AccFunMap)
- end, Map, SCC),
+ end, Map, ToSolve),
Map1 = enter_type_lists(Vars, RecTypes, CleanMap),
?debug("Checking SCC: ~w\n", [[debug_lookup_name(F) || F <- SCC]]),
- FunSet = ordsets:from_list([t_var_name(F) || F <- SCC]),
- Map2 =
- case Parallel of
- false -> solve_whole_scc(SCC, Map1, State);
- SplitSCC -> solve_whole_scc_parallel(SplitSCC, Map1, State)
- end,
- case maps_are_equal(Map2, Map, FunSet) of
+ SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end,
+ Map2 = lists:foldl(SolveFun, Map1, ToSolve),
+ Updated = updated_vars_only(Vars, Map, Map2),
+ case Updated =:= [] of
true ->
?debug("SCC ~w reached fixpoint\n", [SCC]),
NewTypes = unsafe_lookup_type_list(Funs, Map2),
@@ -1694,130 +1854,21 @@ solve_scc(SCC, Parallel, Map, State, TryingUnit) ->
true -> t_fun(t_fun_args(T), t_unit())
end || T <- NewTypes],
Map3 = enter_type_lists(Funs, UnitTypes, Map2),
- solve_scc(SCC, Parallel, Map3, State, true);
+ solve_scc(SCC, Map3, State, Users, SCC, true);
false ->
- case Parallel of
- false -> true;
- _ -> dispose_state(State)
- end,
Map2
end;
false ->
?debug("SCC ~w did not reach fixpoint\n", [SCC]),
- solve_scc(SCC, Parallel, Map2, State, TryingUnit)
- end.
-
-solve_whole_scc(SCC, Map, State) ->
- SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end,
- lists:foldl(SolveFun, Map, SCC).
-
-%%------------------------------------------------------------------------------
-
--define(worth_it, 42).
-
-parallel_split(SCC) ->
- Length = length(SCC),
- case Length > 2*?worth_it of
- false -> false;
- true ->
- case min(dialyzer_utils:parallelism(), 8) of
- 1 -> false;
- CPUs ->
- FullShare = Length div CPUs + 1,
- Unit = max(FullShare, ?worth_it),
- split(SCC, Unit, [])
- end
- end.
-
-minimize_state(#state{
- cmap = {d, CMap},
- fun_map = FunMap,
- fun_arities = FunArities,
- self_rec = SelfRec,
- prop_types = {d, PropTypes},
- solvers = Solvers
- }) ->
- Opts = [{read_concurrency, true}],
- ETSCMap = ets:new(cmap, Opts),
- ETSPropTypes = ets:new(prop_types, Opts),
- true = ets:insert(ETSCMap, dict:to_list(CMap)),
- true = ets:insert(ETSPropTypes, dict:to_list(PropTypes)),
- #state
- {cmap = {e, ETSCMap},
- fun_map = FunMap,
- fun_arities = FunArities,
- self_rec = SelfRec,
- prop_types = {e, ETSPropTypes},
- solvers = Solvers,
- callgraph = undefined,
- plt = undefined,
- mfas = []
- }.
-
-dispose_state(#state{cmap = {e, ETSCMap},
- prop_types = {e, ETSPropTypes}}) ->
- true = ets:delete(ETSCMap),
- true = ets:delete(ETSPropTypes).
-
-solve_whole_scc_parallel(SplitSCC, Map, State) ->
- Workers = spawn_workers(SplitSCC, Map, State),
- wait_results(Workers, Map, fold_res_fun(State)).
-
-spawn_workers(SplitSCC, Map, State) ->
- Spawner = solve_scc_spawner(self(), Map, State),
- lists:foreach(Spawner, SplitSCC),
- length(SplitSCC).
-
-wait_results(0, Map, _FoldResFun) ->
- Map;
-wait_results(Pending, Map, FoldResFun) ->
- Res = receive_scc_result(),
- NewMap = lists:foldl(FoldResFun, Map, Res),
- wait_results(Pending-1, NewMap, FoldResFun).
-
-solve_scc_spawner(Parent, Map, State) ->
- fun(SCCPart) ->
- spawn_link(fun() -> solve_scc_worker(Parent, SCCPart, Map, State) end)
- end.
-
-split([], _Unit, Acc) ->
- Acc;
-split(List, Unit, Acc) ->
- {Taken, Rest} =
- try
- lists:split(Unit, List)
- catch
- _:_ -> {List, []}
- end,
- split(Rest, Unit, [Taken|Acc]).
-
-solve_scc_worker(Parent, SCCPart, Map, State) ->
- SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end,
- FinalMap = lists:foldl(SolveFun, Map, SCCPart),
- Res =
- [{F, t_limit(unsafe_lookup_type(F, FinalMap), ?TYPE_LIMIT)} ||
- F <- SCCPart],
- send_scc_result(Parent, Res).
-
-fold_res_fun(State) ->
- fun({F, Type}, Map) ->
- case state__get_rec_var(F, State) of
- {ok, R} ->
- enter_type(R, Type, enter_type(F, Type, Map));
- error ->
- enter_type(F, Type, Map)
- end
+ ToSolve1 = affected(Updated, Users),
+ solve_scc(SCC, Map2, State, Users, ToSolve1, TryingUnit)
end.
-receive_scc_result() ->
- receive
- {scc_fun, Res} -> Res
- end.
-
-send_scc_result(Parent, Res) ->
- Parent ! {scc_fun, Res}.
-
-%%------------------------------------------------------------------------------
+affected(Updated, Users) ->
+ lists:umerge([case lists:keyfind(V, 1, Users) of
+ {V, Vs} -> Vs;
+ false -> []
+ end || V <- Updated]).
scc_fold_fun(F, FunMap, State) ->
Deps = get_deps(state__get_cs(F, State)),
@@ -1859,7 +1910,7 @@ solver(Solver, SolveFun) ->
solve_fun(v1, _Fun, Cs, FunMap, State) ->
fun() ->
- {ok, _MapDict, NewMap} = solve_ref_or_list(Cs, FunMap, dict:new(), State),
+ {ok, _MapDict, NewMap} = solve_ref_or_list(Cs, FunMap, map_new(), State),
{ok, NewMap}
end;
solve_fun(v2, Fun, _Cs, FunMap, State) ->
@@ -1899,8 +1950,8 @@ sane_maps(Map1, Map2, Keys, _S1, _S2) ->
%% Solver v2
--record(v2_state, {constr_data = dict:new() :: dict:dict(),
- state :: #state{}}).
+-record(v2_state, {constr_data = maps:new() :: map(),
+ state :: state()}).
v2_solve_ref(Fun, Map, State) ->
V2State = #v2_state{state = State},
@@ -2061,30 +2112,30 @@ v2_solve_disj(Is, [C|Cs], I, Map, V2State, UL, MapL, Eval, Uneval0, Failed) ->
v2_solve_disj(Is, Cs, I+1, Map, V2State, UL, MapL, Eval, Uneval, Failed).
save_local_map(#v2_state{constr_data = ConData}=V2State, Id, U, Map) ->
- Part0 = [{V,dict:fetch(V, Map)} || V <- U],
+ Part0 = [{V,maps:get(V, Map)} || V <- U],
Part1 =
- case dict:find(Id, ConData) of
+ case maps:find(Id, ConData) of
error -> []; % cannot happen
{ok, {Part2,[]}} -> Part2
end,
?debug("save local map Id=~w:\n", [Id]),
Part = lists:ukeymerge(1, lists:keysort(1, Part0), Part1),
- pp_map("New Part", dict:from_list(Part0)),
- pp_map("Old Part", dict:from_list(Part1)),
- pp_map(" => Part", dict:from_list(Part)),
- V2State#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)}.
+ pp_map("New Part", maps:from_list(Part0)),
+ pp_map("Old Part", maps:from_list(Part1)),
+ pp_map(" => Part", maps:from_list(Part)),
+ V2State#v2_state{constr_data = maps:put(Id, {Part,[]}, ConData)}.
restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) ->
- case dict:find(Id, ConData) of
+ case maps:find(Id, ConData) of
error -> Map0;
{ok, failed} -> Map0;
{ok, {[],_}} -> Map0;
{ok, {Part0,U}} ->
Part = [KV || {K,_V} = KV <- Part0, not lists:member(K, U)],
?debug("restore local map Id=~w U=~w\n", [Id, U]),
- pp_map("Part", dict:from_list(Part)),
+ pp_map("Part", maps:from_list(Part)),
pp_map("Map0", Map0),
- Map = lists:foldl(fun({K,V}, D) -> dict:store(K, V, D) end, Map0, Part),
+ Map = lists:foldl(fun({K,V}, D) -> maps:put(K, V, D) end, Map0, Part),
pp_map("Map", Map),
Map
end.
@@ -2164,31 +2215,26 @@ report_detected_loop(_) ->
add_mask_to_flags(Flags, [Im|M], I, L) when I > Im ->
add_mask_to_flags(Flags, M, I, [Im|L]);
add_mask_to_flags(Flags, [_|M], _I, L) ->
- {lists:umerge(Flags, M), lists:reverse(L)}.
+ {lists:umerge(M, Flags), lists:reverse(L)}.
-get_mask(V, {d, Masks}) ->
- case dict:find(V, Masks) of
+get_mask(V, Masks) ->
+ case maps:find(V, Masks) of
error -> [];
{ok, M} -> M
- end;
-get_mask(V, Masks) ->
- case lists:keyfind(V, 1, Masks) of
- false -> [];
- {V, M} -> M
end.
get_flags(#v2_state{constr_data = ConData}=V2State0, C) ->
#constraint_list{id = Id, list = Cs, masks = Masks} = C,
- case dict:find(Id, ConData) of
+ case maps:find(Id, ConData) of
error ->
?debug("get_flags Id=~w Flags=all ~w\n", [Id, length(Cs)]),
- V2State = V2State0#v2_state{constr_data = dict:store(Id, {[],[]}, ConData)},
+ V2State = V2State0#v2_state{constr_data = maps:put(Id, {[],[]}, ConData)},
{V2State, lists:seq(1, length(Cs))};
{ok, failed} ->
{V2State0, failed_list};
{ok, {Part,U}} when U =/= [] ->
?debug("get_flags Id=~w U=~w\n", [Id, U]),
- V2State = V2State0#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)},
+ V2State = V2State0#v2_state{constr_data = maps:put(Id, {Part,[]}, ConData)},
save_updated_vars_list(Cs, vars_per_child(U, Masks), V2State)
end.
@@ -2217,13 +2263,13 @@ save_updated_vars(#constraint_ref{id = Id}, U, V2State) ->
save_updated_vars1(V2State, C, U) ->
#v2_state{constr_data = ConData} = V2State,
#constraint_list{id = Id} = C,
- case dict:find(Id, ConData) of
+ case maps:find(Id, ConData) of
error -> V2State; % error means everything is flagged
{ok, failed} -> V2State;
{ok, {Part,U0}} ->
%% Duplicates are not so common; let masks/2 remove them.
U1 = U ++ U0,
- V2State#v2_state{constr_data = dict:store(Id, {Part,U1}, ConData)}
+ V2State#v2_state{constr_data = maps:put(Id, {Part,U1}, ConData)}
end.
-ifdef(DEBUG).
@@ -2233,12 +2279,12 @@ pp_constr_data(_Tag, #v2_state{constr_data = D}) ->
case _PartU of
{_Part, _U} ->
io:format("Id: ~w Vars: ~w\n", [_Id, _U]),
- [pp_map("Part", dict:from_list(_Part)) || _Part =/= []];
+ [pp_map("Part", maps:from_list(_Part)) || _Part =/= []];
failed ->
io:format("Id: ~w failed list\n", [_Id])
end
end ||
- {_Id, _PartU} <- lists:keysort(1, dict:to_list(D))],
+ {_Id, _PartU} <- lists:keysort(1, maps:to_list(D))],
ok.
-else.
@@ -2248,17 +2294,17 @@ pp_constr_data(_Tag, _V2State) ->
failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}=V2State) ->
?debug("error list ~w~n", [Id]),
- V2State#v2_state{constr_data = dict:store(Id, failed, D)}.
+ V2State#v2_state{constr_data = maps:put(Id, failed, D)}.
is_failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}) ->
- dict:find(Id, D) =:= {ok, failed}.
+ maps:find(Id, D) =:= {ok, failed}.
%% Solver v1
solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
Map, MapDict, State) ->
{OldLocalMap, Check} =
- case dict:find(Id, MapDict) of
+ case maps:find(Id, MapDict) of
error -> {map_new(), false};
{ok, M} -> {M, true}
end,
@@ -2304,12 +2350,12 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
{ok, Var} -> enter_type(Var, FunType, NewMap1);
error -> NewMap1
end,
- {ok, dict:store(Id, NewMap2, NewMapDict), NewMap2}
+ {ok, maps:put(Id, NewMap2, NewMapDict), NewMap2}
end;
solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id},
Map, MapDict, State) ->
{OldLocalMap, Check} =
- case dict:find(Id, MapDict) of
+ case maps:find(Id, MapDict) of
error -> {map_new(), false};
{ok, M} -> {M, true}
end,
@@ -2359,7 +2405,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) ->
solve_clist(Cs, conj, Id, Deps, MapDict, Map, State) ->
case solve_cs(Cs, Map, MapDict, State) of
{error, NewMapDict} ->
- {error, dict:store(Id, error, NewMapDict)};
+ {error, maps:put(Id, error, NewMapDict)};
{ok, NewMapDict, NewMap} = Ret ->
case Cs of
[_] ->
@@ -2367,7 +2413,7 @@ solve_clist(Cs, conj, Id, Deps, MapDict, Map, State) ->
Ret;
_ ->
case maps_are_equal(Map, NewMap, Deps) of
- true -> {ok, dict:store(Id, NewMap, NewMapDict), NewMap};
+ true -> {ok, maps:put(Id, NewMap, NewMapDict), NewMap};
false -> solve_clist(Cs, conj, Id, Deps, NewMapDict, NewMap, State)
end
end
@@ -2381,10 +2427,10 @@ solve_clist(Cs, disj, Id, _Deps, MapDict, Map, State) ->
end,
{Maps, NewMapDict} = lists:mapfoldl(Fun, MapDict, Cs),
case [X || {ok, X} <- Maps] of
- [] -> {error, dict:store(Id, error, NewMapDict)};
+ [] -> {error, maps:put(Id, error, NewMapDict)};
MapList ->
NewMap = join_maps(MapList),
- {ok, dict:store(Id, NewMap, NewMapDict), NewMap}
+ {ok, maps:put(Id, NewMap, NewMapDict), NewMap}
end.
solve_cs([#constraint_ref{} = C|Tail], Map, MapDict, State) ->
@@ -2465,7 +2511,7 @@ report_failed_constraint(_C, _Map) ->
%% ============================================================================
map_new() ->
- dict:new().
+ maps:new().
join_maps([Map]) ->
Map;
@@ -2475,9 +2521,9 @@ join_maps(Maps) ->
constrained_keys(Maps) ->
lists:foldl(fun(TmpMap, AccKeys) ->
- [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)]
+ [Key || Key <- AccKeys, maps:is_key(Key, TmpMap)]
end,
- dict:fetch_keys(hd(Maps)), tl(Maps)).
+ maps:keys(hd(Maps)), tl(Maps)).
join_maps([Key|Left], Maps = [Map|MapsLeft], AccMap) ->
NewType = join_one_key(Key, MapsLeft, lookup_type(Key, Map)),
@@ -2523,11 +2569,11 @@ prune_keys(Map1, Map2, Deps) ->
NofDeps = length(Deps),
case NofDeps > ?PRUNE_LIMIT of
true ->
- Keys1 = dict:fetch_keys(Map1),
+ Keys1 = maps:keys(Map1),
case length(Keys1) > NofDeps of
true ->
Set1 = lists:sort(Keys1),
- Set2 = lists:sort(dict:fetch_keys(Map2)),
+ Set2 = lists:sort(maps:keys(Map2)),
ordsets:intersection(ordsets:union(Set1, Set2), Deps);
false ->
Deps
@@ -2548,7 +2594,7 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
true -> ok;
false -> ?debug("LimitedVal ~s\n", [format_type(LimitedVal)])
end,
- case dict:find(Key, Map) of
+ case maps:find(Key, Map) of
{ok, Value} ->
case is_equal(Value, LimitedVal) of
true -> Map;
@@ -2582,16 +2628,16 @@ enter_type2(Key, Val, Map) ->
map_store(Key, Val, Map) ->
?debug("Storing ~w :: ~s\n", [Key, format_type(Val)]),
- dict:store(Key, Val, Map).
+ maps:put(Key, Val, Map).
erase_type(Key, Map) ->
- dict:erase(Key, Map).
+ maps:remove(Key, Map).
lookup_type_list(List, Map) ->
[lookup_type(X, Map) || X <- List].
unsafe_lookup_type(Key, Map) ->
- case dict:find(t_var_name(Key), Map) of
+ case maps:find(t_var_name(Key), Map) of
{ok, Type} -> Type;
error -> t_none()
end.
@@ -2600,7 +2646,7 @@ unsafe_lookup_type_list(List, Map) ->
[unsafe_lookup_type(X, Map) || X <- List].
lookup_type(Key, Map) when is_integer(Key) ->
- case dict:find(Key, Map) of
+ case maps:find(Key, Map) of
error -> t_any();
{ok, Val} -> Val
end;
@@ -2648,7 +2694,7 @@ is_equal(Type1, Type2) ->
pp_map(_S, _Map) ->
?debug("\t~s: ~p\n",
[_S, [{X, lists:flatten(format_type(Y))} ||
- {X, Y} <- lists:keysort(1, dict:to_list(_Map))]]).
+ {X, Y} <- lists:keysort(1, maps:to_list(_Map))]]).
%% ============================================================================
%%
@@ -2658,7 +2704,7 @@ pp_map(_S, _Map) ->
new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes, Solvers) ->
List = [{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0],
- NameMap = dict:from_list(List),
+ NameMap = maps:from_list(List),
MFAs = [MFA || {MFA, _Var} <- List],
SCC = [mk_var(Fun) || {_MFA, {_Var, Fun}, _Rec} <- SCC0],
SelfRec =
@@ -2672,7 +2718,7 @@ new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes, Solvers) ->
_Many -> false
end,
#state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel,
- prop_types = {d, PropTypes}, plt = Plt, scc = ordsets:from_list(SCC),
+ prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC),
mfas = MFAs, self_rec = SelfRec, solvers = Solvers}.
state__set_rec_dict(State, RecDict) ->
@@ -2700,15 +2746,15 @@ state__get_fun_prototype(Op, Arity, State) ->
end.
state__lookup_rec_var_in_scope(MFA, #state{name_map = NameMap}) ->
- dict:find(MFA, NameMap).
+ maps:find(MFA, NameMap).
state__store_fun_arity(Tree, #state{fun_arities = Map} = State) ->
Arity = length(cerl:fun_vars(Tree)),
Id = mk_var(Tree),
- State#state{fun_arities = dict:store(Id, Arity, Map)}.
+ State#state{fun_arities = maps:put(Id, Arity, Map)}.
state__fun_arity(Id, #state{fun_arities = Map}) ->
- dict:fetch(Id, Map).
+ maps:get(Id, Map).
state__lookup_undef_var(Tree, #state{callgraph = CG, plt = Plt}) ->
Label = cerl_trees:get_label(Tree),
@@ -2768,21 +2814,14 @@ state__plt(#state{plt = PLT}) ->
state__new_constraint_context(State) ->
State#state{cs = []}.
-state__prop_domain(FunLabel, #state{prop_types = {e, ETSPropTypes}}) ->
- try ets:lookup_element(ETSPropTypes, FunLabel, 2) of
- {_Range_Fun, Dom} -> {ok, Dom};
- FunType -> {ok, t_fun_args(FunType)}
- catch
- _:_ -> error
- end;
-state__prop_domain(FunLabel, #state{prop_types = {d, PropTypes}}) ->
+state__prop_domain(FunLabel, #state{prop_types = PropTypes}) ->
case dict:find(FunLabel, PropTypes) of
error -> error;
{ok, {_Range_Fun, Dom}} -> {ok, Dom};
{ok, FunType} -> {ok, t_fun_args(FunType)}
end.
-state__add_prop_constrs(Tree, #state{prop_types = {d, PropTypes}} = State) ->
+state__add_prop_constrs(Tree, #state{prop_types = PropTypes} = State) ->
Label = cerl_trees:get_label(Tree),
case dict:find(Label, PropTypes) of
error -> State;
@@ -2845,14 +2884,12 @@ state__mk_vars(N, #state{next_label = NL} = State) ->
Vars = [t_var(X) || X <- lists:seq(NL, NewLabel-1)],
{State#state{next_label = NewLabel}, Vars}.
-state__store_constrs(Id, Cs, #state{cmap = {d, Dict}} = State) ->
- NewDict = dict:store(Id, Cs, Dict),
- State#state{cmap = {d, NewDict}}.
+state__store_constrs(Id, Cs, #state{cmap = Map} = State) ->
+ NewMap = maps:put(Id, Cs, Map),
+ State#state{cmap = NewMap}.
-state__get_cs(Var, #state{cmap = {e, ETSDict}}) ->
- ets:lookup_element(ETSDict, Var, 2);
-state__get_cs(Var, #state{cmap = {d, Dict}}) ->
- dict:fetch(Var, Dict).
+state__get_cs(Var, #state{cmap = Map}) ->
+ maps:get(Var, Map).
state__is_self_rec(Fun, #state{self_rec = SelfRec}) ->
not (SelfRec =:= 'false') andalso is_equal(Fun, SelfRec).
@@ -2861,15 +2898,12 @@ state__store_funs(Vars0, Funs0, #state{fun_map = Map} = State) ->
debug_make_name_map(Vars0, Funs0),
Vars = mk_var_list(Vars0),
Funs = mk_var_list(Funs0),
- NewMap = lists:foldl(fun({Var, Fun}, MP) -> orddict:store(Var, Fun, MP) end,
+ NewMap = lists:foldl(fun({Var, Fun}, MP) -> maps:put(Fun, Var, MP) end,
Map, lists:zip(Vars, Funs)),
State#state{fun_map = NewMap}.
state__get_rec_var(Fun, #state{fun_map = Map}) ->
- case [V || {V, FV} <- Map, FV =:= Fun] of
- [Var] -> {ok, Var};
- [] -> error
- end.
+ maps:find(Fun, Map).
state__finalize(State) ->
State1 = enumerate_constraints(State),
@@ -2936,13 +2970,13 @@ mk_fun_var(Fun, Types) ->
-endif.
--spec get_deps(constr()) -> [dep()].
+-spec get_deps(constr()) -> deps().
get_deps(#constraint{deps = D}) -> D;
get_deps(#constraint_list{deps = D}) -> D;
get_deps(#constraint_ref{deps = D}) -> D.
--spec find_constraint_deps([fvar_or_type()]) -> [dep()].
+-spec find_constraint_deps([fvar_or_type()]) -> deps().
find_constraint_deps(List) ->
ordsets:from_list(find_constraint_deps(List, [])).
@@ -2975,13 +3009,24 @@ mk_constraint_ref(Id, Deps) ->
mk_constraint_list(Type, List) ->
List1 = ordsets:from_list(lift_lists(Type, List)),
- List2 = ordsets:filter(fun(X) -> get_deps(X) =/= [] end, List1),
- Deps = calculate_deps(List2),
+ case Type of
+ conj ->
+ List2 = ordsets:filter(fun(X) -> get_deps(X) =/= [] end, List1),
+ mk_constraint_list_cont(Type, List2);
+ disj ->
+ case lists:any(fun(X) -> get_deps(X) =:= [] end, List1) of
+ true -> mk_constraint_list_cont(Type, [mk_constraint_any(eq)]);
+ false -> mk_constraint_list_cont(Type, List1)
+ end
+ end.
+
+mk_constraint_list_cont(Type, List) ->
+ Deps = calculate_deps(List),
case Deps =:= [] of
true -> #constraint_list{type = conj,
list = [mk_constraint_any(eq)],
deps = []};
- false -> #constraint_list{type = Type, list = List2, deps = Deps}
+ false -> #constraint_list{type = Type, list = List, deps = Deps}
end.
lift_lists(Type, List) ->
@@ -3179,18 +3224,11 @@ order_fun_constraints([], Funs, Acc, State) ->
update_masks(C, Masks) ->
C#constraint_list{masks = Masks}.
--define(VARS_LIMIT, 50).
-
calculate_masks([C|Cs], I, L0) ->
calculate_masks(Cs, I+1, [{V, I} || V <- get_deps(C)] ++ L0);
calculate_masks([], _I, L) ->
M = family(L),
- case length(M) > ?VARS_LIMIT of
- true ->
- {d, dict:from_list(M)};
- false ->
- M
- end.
+ maps:from_list(M).
%% ============================================================================
%%
@@ -3263,6 +3301,15 @@ find_constraint(Tuple, [#constraint_list{list = List}|Cs]) ->
find_constraint(Tuple, [_|Cs]) ->
find_constraint(Tuple, Cs).
+-spec fold_literal_maybe_match(cerl:cerl(), state()) -> cerl:cerl().
+
+fold_literal_maybe_match(Tree0, State) ->
+ Tree1 = cerl:fold_literal(Tree0),
+ case state__is_in_match(State) of
+ false -> Tree1;
+ true -> dialyzer_utils:refold_pattern(Tree1)
+ end.
+
lookup_record(Records, Tag, Arity) ->
case erl_types:lookup_record(Tag, Arity, Records) of
{ok, Fields} ->
@@ -3309,7 +3356,7 @@ join_chars([H|T], Sep) ->
[H|[[Sep,X] || X <- T]].
debug_lookup_name(Var) ->
- case dict:find(t_var_name(Var), get(dialyzer_typesig_map)) of
+ case maps:find(t_var_name(Var), get(dialyzer_typesig_map)) of
error -> Var;
{ok, Name} -> Name
end.
@@ -3319,7 +3366,7 @@ debug_lookup_name(Var) ->
debug_make_name_map(Vars, Funs) ->
Map = get(dialyzer_typesig_map),
NewMap =
- if Map =:= undefined -> debug_make_name_map(Vars, Funs, dict:new());
+ if Map =:= undefined -> debug_make_name_map(Vars, Funs, maps:new());
true -> debug_make_name_map(Vars, Funs, Map)
end,
put(dialyzer_typesig_map, NewMap).
@@ -3327,7 +3374,7 @@ debug_make_name_map(Vars, Funs) ->
debug_make_name_map([Var|VarLeft], [Fun|FunLeft], Map) ->
Name = {cerl:fname_id(Var), cerl:fname_arity(Var)},
FunLabel = cerl_trees:get_label(Fun),
- debug_make_name_map(VarLeft, FunLeft, dict:store(FunLabel, Name, Map));
+ debug_make_name_map(VarLeft, FunLeft, maps:put(FunLabel, Name, Map));
debug_make_name_map([], [], Map) ->
Map.
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 5fc1c0e691..d37701f03b 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -49,6 +49,7 @@
process_record_remote_types/1,
sets_filter/2,
src_compiler_opts/0,
+ refold_pattern/1,
parallelism/0,
family/1
]).
@@ -752,6 +753,13 @@ pp_hook(Node, Ctxt, Cont) ->
pp_binary(Node, Ctxt, Cont);
bitstr ->
pp_segment(Node, Ctxt, Cont);
+ map ->
+ pp_map(Node, Ctxt, Cont);
+ literal ->
+ case is_map(cerl:concrete(Node)) of
+ true -> pp_map(Node, Ctxt, Cont);
+ false -> Cont(Node, Ctxt)
+ end;
_ ->
Cont(Node, Ctxt)
end.
@@ -832,6 +840,87 @@ pp_atom(Atom) ->
String = atom_to_list(cerl:atom_val(Atom)),
prettypr:text(String).
+pp_map(Node, Ctxt, Cont) ->
+ Arg = cerl:map_arg(Node),
+ Before = case cerl:is_c_map_empty(Arg) of
+ true -> prettypr:floating(prettypr:text("#{"));
+ false ->
+ prettypr:beside(Cont(Arg,Ctxt),
+ prettypr:floating(prettypr:text("#{")))
+ end,
+ prettypr:beside(
+ Before, prettypr:beside(
+ prettypr:par(seq(cerl:map_es(Node),
+ prettypr:floating(prettypr:text(",")),
+ Ctxt, Cont)),
+ prettypr:floating(prettypr:text("}")))).
+
+seq([H | T], Separator, Ctxt, Fun) ->
+ case T of
+ [] -> [Fun(H, Ctxt)];
+ _ -> [prettypr:beside(Fun(H, Ctxt), Separator)
+ | seq(T, Separator, Ctxt, Fun)]
+ end;
+seq([], _, _, _) ->
+ [prettypr:empty()].
+
+%%------------------------------------------------------------------------------
+
+-spec refold_pattern(cerl:cerl()) -> cerl:cerl().
+
+refold_pattern(Pat) ->
+ %% Avoid the churn of unfolding and refolding
+ case cerl:is_literal(Pat) andalso find_map(cerl:concrete(Pat)) of
+ true ->
+ Tree = refold_concrete_pat(cerl:concrete(Pat)),
+ PatAnn = cerl:get_ann(Pat),
+ case proplists:is_defined(label, PatAnn) of
+ %% Literals are not normally annotated with a label, but can be if, for
+ %% example, they were created by cerl:fold_literal/1.
+ true -> cerl:set_ann(Tree, PatAnn);
+ false ->
+ [{label, Label}] = cerl:get_ann(Tree),
+ cerl:set_ann(Tree, [{label, Label}|PatAnn])
+ end;
+ false -> Pat
+ end.
+
+find_map(#{}) -> true;
+find_map(Tuple) when is_tuple(Tuple) -> find_map(tuple_to_list(Tuple));
+find_map([H|T]) -> find_map(H) orelse find_map(T);
+find_map(_) -> false.
+
+refold_concrete_pat(Val) ->
+ case Val of
+ _ when is_tuple(Val) ->
+ Els = lists:map(fun refold_concrete_pat/1, tuple_to_list(Val)),
+ case lists:all(fun cerl:is_literal/1, Els) of
+ true -> cerl:abstract(Val);
+ false -> label(cerl:c_tuple_skel(Els))
+ end;
+ [H|T] ->
+ case cerl:is_literal(HP=refold_concrete_pat(H))
+ and cerl:is_literal(TP=refold_concrete_pat(T))
+ of
+ true -> cerl:abstract(Val);
+ false -> label(cerl:c_cons_skel(HP, TP))
+ end;
+ M when is_map(M) ->
+ %% Map patterns are not generated by the parser(!), but they have a
+ %% property we want, namely that they are never folded into literals.
+ %% N.B.: The key in a map pattern is an expression, *not* a pattern.
+ label(cerl:c_map_pattern([cerl:c_map_pair_exact(cerl:abstract(K),
+ refold_concrete_pat(V))
+ || {K, V} <- maps:to_list(M)]));
+ _ ->
+ cerl:abstract(Val)
+ end.
+
+label(Tree) ->
+ %% Sigh
+ Label = -erlang:unique_integer([positive]),
+ cerl:set_ann(Tree, [{label, Label}]).
+
%%------------------------------------------------------------------------------
-spec parallelism() -> integer().
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
index 89eb295604..638d031923 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
@@ -1,2 +1,2 @@
-supervisor_incorrect_return.erl:14: The inferred return type of init/1 ({'ok',{{'one_against_one',0,1},[{_,_,_,_,_,_},...]}}) has nothing in common with 'ignore' | {'ok',{{'one_for_all',non_neg_integer(),pos_integer()} | {'one_for_one',non_neg_integer(),pos_integer()} | {'rest_for_one',non_neg_integer(),pos_integer()} | {'simple_one_for_one',non_neg_integer(),pos_integer()} | #{},[{_,{atom(),atom(),'undefined' | [any()]},'permanent' | 'temporary' | 'transient','brutal_kill' | 'infinity' | non_neg_integer(),'supervisor' | 'worker','dynamic' | [atom()]} | #{}]}}, which is the expected return type for the callback of supervisor behaviour
+supervisor_incorrect_return.erl:14: The inferred return type of init/1 ({'ok',{{'one_against_one',0,1},[{_,_,_,_,_,_},...]}}) has nothing in common with 'ignore' | {'ok',{{'one_for_all',non_neg_integer(),pos_integer()} | {'one_for_one',non_neg_integer(),pos_integer()} | {'rest_for_one',non_neg_integer(),pos_integer()} | {'simple_one_for_one',non_neg_integer(),pos_integer()} | #{'intensity'=>non_neg_integer(), 'period'=>pos_integer(), 'strategy'=>'one_for_all' | 'one_for_one' | 'rest_for_one' | 'simple_one_for_one'},[{_,{atom(),atom(),'undefined' | [any()]},'permanent' | 'temporary' | 'transient','brutal_kill' | 'infinity' | non_neg_integer(),'supervisor' | 'worker','dynamic' | [atom()]} | #{'id':=_, 'start':={atom(),atom(),'undefined' | [any()]}, 'modules'=>'dynamic' | [atom()], 'restart'=>'permanent' | 'temporary' | 'transient', 'shutdown'=>'brutal_kill' | 'infinity' | non_neg_integer(), 'type'=>'supervisor' | 'worker'}]}}, which is the expected return type for the callback of supervisor behaviour
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/compile_flags.hrl b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/compile_flags.hrl
new file mode 100644
index 0000000000..e5ee44ace1
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/compile_flags.hrl
@@ -0,0 +1,2 @@
+-define(AT_LEAST_19, 1).
+-define(AT_LEAST_17, 1).
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_common.hrl b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_common.hrl
new file mode 100644
index 0000000000..c10626c5cc
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_common.hrl
@@ -0,0 +1,55 @@
+%%% Copyright 2010-2013 Manolis Papadakis <[email protected]>,
+%%% Eirini Arvaniti <[email protected]>
+%%% and Kostis Sagonas <[email protected]>
+%%%
+%%% This file is part of PropEr.
+%%%
+%%% PropEr is free software: you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation, either version 3 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% PropEr is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with PropEr. If not, see <http://www.gnu.org/licenses/>.
+
+%%% @copyright 2010-2013 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
+%%% @version {@version}
+%%% @author Manolis Papadakis
+%%% @doc Common parts of user and internal header files
+
+
+%%------------------------------------------------------------------------------
+%% Test generation macros
+%%------------------------------------------------------------------------------
+
+-define(FORALL(X,RawType,Prop), proper:forall(RawType,fun(X) -> Prop end)).
+-define(IMPLIES(Pre,Prop), proper:implies(Pre,?DELAY(Prop))).
+-define(WHENFAIL(Action,Prop), proper:whenfail(?DELAY(Action),?DELAY(Prop))).
+-define(TRAPEXIT(Prop), proper:trapexit(?DELAY(Prop))).
+-define(TIMEOUT(Limit,Prop), proper:timeout(Limit,?DELAY(Prop))).
+%% TODO: -define(ALWAYS(Tests,Prop), proper:always(Tests,?DELAY(Prop))).
+%% TODO: -define(SOMETIMES(Tests,Prop), proper:sometimes(Tests,?DELAY(Prop))).
+
+
+%%------------------------------------------------------------------------------
+%% Generator macros
+%%------------------------------------------------------------------------------
+
+-define(FORCE(X), (X)()).
+-define(DELAY(X), fun() -> X end).
+-define(LAZY(X), proper_types:lazy(?DELAY(X))).
+-define(SIZED(SizeArg,Gen), proper_types:sized(fun(SizeArg) -> Gen end)).
+-define(LET(X,RawType,Gen), proper_types:bind(RawType,fun(X) -> Gen end,false)).
+-define(SHRINK(Gen,AltGens),
+ proper_types:shrinkwith(?DELAY(Gen),?DELAY(AltGens))).
+-define(LETSHRINK(Xs,RawType,Gen),
+ proper_types:bind(RawType,fun(Xs) -> Gen end,true)).
+-define(SUCHTHAT(X,RawType,Condition),
+ proper_types:add_constraint(RawType,fun(X) -> Condition end,true)).
+-define(SUCHTHATMAYBE(X,RawType,Condition),
+ proper_types:add_constraint(RawType,fun(X) -> Condition end,false)).
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_gen.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_gen.erl
new file mode 100644
index 0000000000..b64a139e4d
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_gen.erl
@@ -0,0 +1,611 @@
+%%% Copyright 2010-2015 Manolis Papadakis <[email protected]>,
+%%% Eirini Arvaniti <[email protected]>
+%%% and Kostis Sagonas <[email protected]>
+%%%
+%%% This file is part of PropEr.
+%%%
+%%% PropEr is free software: you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation, either version 3 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% PropEr is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with PropEr. If not, see <http://www.gnu.org/licenses/>.
+
+%%% @copyright 2010-2015 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
+%%% @version {@version}
+%%% @author Manolis Papadakis
+
+%%% @doc Generator subsystem and generators for basic types.
+%%%
+%%% You can use <a href="#index">these</a> functions to try out the random
+%%% instance generation and shrinking subsystems.
+%%%
+%%% CAUTION: These functions should never be used inside properties. They are
+%%% meant for demonstration purposes only.
+
+-module(proper_gen).
+-export([pick/1, pick/2, pick/3,
+ sample/1, sample/3, sampleshrink/1, sampleshrink/2]).
+
+-export([safe_generate/1]).
+-export([generate/1, normal_gen/1, alt_gens/1, clean_instance/1,
+ get_ret_type/1]).
+-export([integer_gen/3, float_gen/3, atom_gen/1, atom_rev/1, binary_gen/1,
+ binary_rev/1, binary_len_gen/1, bitstring_gen/1, bitstring_rev/1,
+ bitstring_len_gen/1, list_gen/2, distlist_gen/3, vector_gen/2,
+ union_gen/1, weighted_union_gen/1, tuple_gen/1, loose_tuple_gen/2,
+ loose_tuple_rev/2, exactly_gen/1, fixed_list_gen/1, function_gen/2,
+ any_gen/1, native_type_gen/2, safe_weighted_union_gen/1,
+ safe_union_gen/1]).
+
+-export_type([instance/0, imm_instance/0, sized_generator/0, nosize_generator/0,
+ generator/0, reverse_gen/0, combine_fun/0, alt_gens/0]).
+
+-include("proper_internal.hrl").
+
+%%-----------------------------------------------------------------------------
+%% Types
+%%-----------------------------------------------------------------------------
+
+%% TODO: update imm_instance() when adding more types: be careful when reading
+%% anything that returns it
+%% @private_type
+-type imm_instance() :: proper_types:raw_type()
+ | instance()
+ | {'$used', imm_instance(), imm_instance()}
+ | {'$to_part', imm_instance()}.
+-type instance() :: term().
+%% A value produced by the random instance generator.
+-type error_reason() :: 'arity_limit' | 'cant_generate' | {'typeserver',term()}.
+
+%% @private_type
+-type sized_generator() :: fun((size()) -> imm_instance()).
+%% @private_type
+-type typed_sized_generator() :: {'typed',
+ fun((proper_types:type(),size()) ->
+ imm_instance())}.
+%% @private_type
+-type nosize_generator() :: fun(() -> imm_instance()).
+%% @private_type
+-type typed_nosize_generator() :: {'typed',
+ fun((proper_types:type()) ->
+ imm_instance())}.
+%% @private_type
+-type generator() :: sized_generator()
+ | typed_sized_generator()
+ | nosize_generator()
+ | typed_nosize_generator().
+%% @private_type
+-type plain_reverse_gen() :: fun((instance()) -> imm_instance()).
+%% @private_type
+-type typed_reverse_gen() :: {'typed',
+ fun((proper_types:type(),instance()) ->
+ imm_instance())}.
+%% @private_type
+-type reverse_gen() :: plain_reverse_gen() | typed_reverse_gen().
+%% @private_type
+-type combine_fun() :: fun((instance()) -> imm_instance()).
+%% @private_type
+-type alt_gens() :: fun(() -> [imm_instance()]).
+%% @private_type
+-type fun_seed() :: {non_neg_integer(),non_neg_integer()}.
+
+
+%%-----------------------------------------------------------------------------
+%% Instance generation functions
+%%-----------------------------------------------------------------------------
+
+%% @private
+-spec safe_generate(proper_types:raw_type()) ->
+ {'ok',imm_instance()} | {'error',error_reason()}.
+safe_generate(RawType) ->
+ try generate(RawType) of
+ ImmInstance -> {ok, ImmInstance}
+ catch
+ throw:'$arity_limit' -> {error, arity_limit};
+ throw:'$cant_generate' -> {error, cant_generate};
+ throw:{'$typeserver',SubReason} -> {error, {typeserver,SubReason}}
+ end.
+
+%% @private
+-spec generate(proper_types:raw_type()) -> imm_instance().
+generate(RawType) ->
+ Type = proper_types:cook_outer(RawType),
+ ok = add_parameters(Type),
+ Instance = generate(Type, get('$constraint_tries'), none),
+ ok = remove_parameters(Type),
+ Instance.
+
+-spec add_parameters(proper_types:type()) -> 'ok'.
+add_parameters(Type) ->
+ case proper_types:find_prop(parameters, Type) of
+ {ok, Params} ->
+ OldParams = erlang:get('$parameters'),
+ case OldParams of
+ undefined ->
+ erlang:put('$parameters', Params);
+ _ ->
+ erlang:put('$parameters', Params ++ OldParams)
+ end,
+ ok;
+ _ ->
+ ok
+ end.
+
+-spec remove_parameters(proper_types:type()) -> 'ok'.
+remove_parameters(Type) ->
+ case proper_types:find_prop(parameters, Type) of
+ {ok, Params} ->
+ AllParams = erlang:get('$parameters'),
+ case AllParams of
+ Params->
+ erlang:erase('$parameters');
+ _ ->
+ erlang:put('$parameters', AllParams -- Params)
+ end,
+ ok;
+ _ ->
+ ok
+ end.
+
+-spec generate(proper_types:type(), non_neg_integer(),
+ 'none' | {'ok',imm_instance()}) -> imm_instance().
+generate(_Type, 0, none) ->
+ throw('$cant_generate');
+generate(_Type, 0, {ok,Fallback}) ->
+ Fallback;
+generate(Type, TriesLeft, Fallback) ->
+ ImmInstance =
+ case proper_types:get_prop(kind, Type) of
+ constructed ->
+ PartsType = proper_types:get_prop(parts_type, Type),
+ Combine = proper_types:get_prop(combine, Type),
+ ImmParts = generate(PartsType),
+ Parts = clean_instance(ImmParts),
+ ImmInstance1 = Combine(Parts),
+ %% TODO: We can just generate the internal type: if it's not
+ %% a type, it will turn into an exactly.
+ ImmInstance2 =
+ case proper_types:is_raw_type(ImmInstance1) of
+ true -> generate(ImmInstance1);
+ false -> ImmInstance1
+ end,
+ {'$used',ImmParts,ImmInstance2};
+ _ ->
+ ImmInstance1 = normal_gen(Type),
+ case proper_types:is_raw_type(ImmInstance1) of
+ true -> generate(ImmInstance1);
+ false -> ImmInstance1
+ end
+ end,
+ case proper_types:satisfies_all(clean_instance(ImmInstance), Type) of
+ {_,true} -> ImmInstance;
+ {true,false} -> generate(Type, TriesLeft - 1, {ok,ImmInstance});
+ {false,false} -> generate(Type, TriesLeft - 1, Fallback)
+ end.
+
+%% @equiv pick(Type, 10)
+-spec pick(Type::proper_types:raw_type()) -> {'ok',instance()} | 'error'.
+pick(RawType) ->
+ pick(RawType, 10).
+
+%% @equiv pick(Type, Size, os:timestamp())
+-spec pick(Type::proper_types:raw_type(), size()) -> {'ok',instance()} | 'error'.
+pick(RawType, Size) ->
+ pick(RawType, Size, os:timestamp()).
+
+%% @doc Generates a random instance of `Type', of size `Size' with seed `Seed'.
+-spec pick(Type::proper_types:raw_type(), size(), seed()) ->
+ {'ok',instance()} | 'error'.
+pick(RawType, Size, Seed) ->
+ proper:global_state_init_size_seed(Size, Seed),
+ case clean_instance(safe_generate(RawType)) of
+ {ok,Instance} = Result ->
+ Msg = "WARNING: Some garbage has been left in the process registry "
+ "and the code server~n"
+ "to allow for the returned function(s) to run normally.~n"
+ "Please run proper:global_state_erase() when done.~n",
+ case contains_fun(Instance) of
+ true -> io:format(Msg, []);
+ false -> proper:global_state_erase()
+ end,
+ Result;
+ {error,Reason} ->
+ proper:report_error(Reason, fun io:format/2),
+ proper:global_state_erase(),
+ error
+ end.
+
+%% @equiv sample(Type, 10, 20)
+-spec sample(Type::proper_types:raw_type()) -> 'ok'.
+sample(RawType) ->
+ sample(RawType, 10, 20).
+
+%% @doc Generates and prints one random instance of `Type' for each size from
+%% `StartSize' up to `EndSize'.
+-spec sample(Type::proper_types:raw_type(), size(), size()) -> 'ok'.
+sample(RawType, StartSize, EndSize) when StartSize =< EndSize ->
+ Tests = EndSize - StartSize + 1,
+ Prop = ?FORALL(X, RawType, begin io:format("~p~n",[X]), true end),
+ Opts = [quiet,{start_size,StartSize},{max_size,EndSize},{numtests,Tests}],
+ _ = proper:quickcheck(Prop, Opts),
+ ok.
+
+%% @equiv sampleshrink(Type, 10)
+-spec sampleshrink(Type::proper_types:raw_type()) -> 'ok'.
+sampleshrink(RawType) ->
+ sampleshrink(RawType, 10).
+
+%% @doc Generates a random instance of `Type', of size `Size', then shrinks it
+%% as far as it goes. The value produced on each step of the shrinking process
+%% is printed on the screen.
+-spec sampleshrink(Type::proper_types:raw_type(), size()) -> 'ok'.
+sampleshrink(RawType, Size) ->
+ proper:global_state_init_size(Size),
+ Type = proper_types:cook_outer(RawType),
+ case safe_generate(Type) of
+ {ok,ImmInstance} ->
+ Shrunk = keep_shrinking(ImmInstance, [], Type),
+ PrintInst = fun(I) -> io:format("~p~n",[clean_instance(I)]) end,
+ lists:foreach(PrintInst, Shrunk);
+ {error,Reason} ->
+ proper:report_error(Reason, fun io:format/2)
+ end,
+ proper:global_state_erase(),
+ ok.
+
+-spec keep_shrinking(imm_instance(), [imm_instance()], proper_types:type()) ->
+ [imm_instance(),...].
+keep_shrinking(ImmInstance, Acc, Type) ->
+ keep_shrinking(ImmInstance, Acc, Type, init).
+
+keep_shrinking(ImmInstance, Acc, Type, State) ->
+ case proper_shrink:shrink(ImmInstance, Type, State) of
+ {[], done} -> %% no more shrinkers
+ lists:reverse([ImmInstance|Acc]);
+ {[], NewState} ->
+ %% try next shrinker
+ keep_shrinking(ImmInstance, Acc, Type, NewState);
+ {[Shrunk|_Rest], _NewState} ->
+ Acc2 = [ImmInstance|Acc],
+ case lists:member(Shrunk, Acc2) of
+ true ->
+ %% Avoid infinite loops
+ lists:reverse(Acc2);
+ false ->
+ keep_shrinking(Shrunk, Acc2, Type)
+ end
+ end.
+
+-spec contains_fun(term()) -> boolean().
+contains_fun(List) when is_list(List) ->
+ proper_arith:safe_any(fun contains_fun/1, List);
+contains_fun(Tuple) when is_tuple(Tuple) ->
+ contains_fun(tuple_to_list(Tuple));
+contains_fun(Fun) when is_function(Fun) ->
+ true;
+contains_fun(_Term) ->
+ false.
+
+
+%%-----------------------------------------------------------------------------
+%% Utility functions
+%%-----------------------------------------------------------------------------
+
+%% @private
+-spec normal_gen(proper_types:type()) -> imm_instance().
+normal_gen(Type) ->
+ case proper_types:get_prop(generator, Type) of
+ {typed, Gen} ->
+ if
+ is_function(Gen, 1) -> Gen(Type);
+ is_function(Gen, 2) -> Gen(Type, proper:get_size(Type))
+ end;
+ Gen ->
+ if
+ is_function(Gen, 0) -> Gen();
+ is_function(Gen, 1) -> Gen(proper:get_size(Type))
+ end
+ end.
+
+%% @private
+-spec alt_gens(proper_types:type()) -> [imm_instance()].
+alt_gens(Type) ->
+ case proper_types:find_prop(alt_gens, Type) of
+ {ok, AltGens} -> ?FORCE(AltGens);
+ error -> []
+ end.
+
+%% @private
+-spec clean_instance(imm_instance()) -> instance().
+clean_instance({'$used',_ImmParts,ImmInstance}) ->
+ clean_instance(ImmInstance);
+clean_instance({'$to_part',ImmInstance}) ->
+ clean_instance(ImmInstance);
+clean_instance(ImmInstance) ->
+ if
+ is_list(ImmInstance) ->
+ %% CAUTION: this must handle improper lists
+ proper_arith:safe_map(fun clean_instance/1, ImmInstance);
+ is_tuple(ImmInstance) ->
+ proper_arith:tuple_map(fun clean_instance/1, ImmInstance);
+ true ->
+ ImmInstance
+ end.
+
+
+%%-----------------------------------------------------------------------------
+%% Basic type generators
+%%-----------------------------------------------------------------------------
+
+%% @private
+-spec integer_gen(size(), proper_types:extint(), proper_types:extint()) ->
+ integer().
+integer_gen(Size, inf, inf) ->
+ proper_arith:rand_int(Size);
+integer_gen(Size, inf, High) ->
+ High - proper_arith:rand_non_neg_int(Size);
+integer_gen(Size, Low, inf) ->
+ Low + proper_arith:rand_non_neg_int(Size);
+integer_gen(Size, Low, High) ->
+ proper_arith:smart_rand_int(Size, Low, High).
+
+%% @private
+-spec float_gen(size(), proper_types:extnum(), proper_types:extnum()) ->
+ float().
+float_gen(Size, inf, inf) ->
+ proper_arith:rand_float(Size);
+float_gen(Size, inf, High) ->
+ High - proper_arith:rand_non_neg_float(Size);
+float_gen(Size, Low, inf) ->
+ Low + proper_arith:rand_non_neg_float(Size);
+float_gen(_Size, Low, High) ->
+ proper_arith:rand_float(Low, High).
+
+%% @private
+-spec atom_gen(size()) -> proper_types:type().
+%% We make sure we never clash with internal atoms by checking that the first
+%% character is not '$'.
+atom_gen(Size) ->
+ ?LET(Str,
+ ?SUCHTHAT(X,
+ proper_types:resize(Size,
+ proper_types:list(proper_types:byte())),
+ X =:= [] orelse hd(X) =/= $$),
+ list_to_atom(Str)).
+
+%% @private
+-spec atom_rev(atom()) -> imm_instance().
+atom_rev(Atom) ->
+ {'$used', atom_to_list(Atom), Atom}.
+
+%% @private
+-spec binary_gen(size()) -> proper_types:type().
+binary_gen(Size) ->
+ ?LET(Bytes,
+ proper_types:resize(Size,
+ proper_types:list(proper_types:byte())),
+ list_to_binary(Bytes)).
+
+%% @private
+-spec binary_rev(binary()) -> imm_instance().
+binary_rev(Binary) ->
+ {'$used', binary_to_list(Binary), Binary}.
+
+%% @private
+-spec binary_len_gen(length()) -> proper_types:type().
+binary_len_gen(Len) ->
+ ?LET(Bytes,
+ proper_types:vector(Len, proper_types:byte()),
+ list_to_binary(Bytes)).
+
+%% @private
+-spec bitstring_gen(size()) -> proper_types:type().
+bitstring_gen(Size) ->
+ ?LET({BytesHead, NumBits, TailByte},
+ {proper_types:resize(Size,proper_types:binary()),
+ proper_types:range(0,7), proper_types:range(0,127)},
+ <<BytesHead/binary, TailByte:NumBits>>).
+
+%% @private
+-spec bitstring_rev(bitstring()) -> imm_instance().
+bitstring_rev(BitString) ->
+ List = bitstring_to_list(BitString),
+ {BytesList, BitsTail} = lists:splitwith(fun erlang:is_integer/1, List),
+ {NumBits, TailByte} = case BitsTail of
+ [] -> {0, 0};
+ [Bits] -> N = bit_size(Bits),
+ <<Byte:N>> = Bits,
+ {N, Byte}
+ end,
+ {'$used',
+ {{'$used',BytesList,list_to_binary(BytesList)}, NumBits, TailByte},
+ BitString}.
+
+%% @private
+-spec bitstring_len_gen(length()) -> proper_types:type().
+bitstring_len_gen(Len) ->
+ BytesLen = Len div 8,
+ BitsLen = Len rem 8,
+ ?LET({BytesHead, NumBits, TailByte},
+ {proper_types:binary(BytesLen), BitsLen,
+ proper_types:range(0, 1 bsl BitsLen - 1)},
+ <<BytesHead/binary, TailByte:NumBits>>).
+
+%% @private
+-spec list_gen(size(), proper_types:type()) -> [imm_instance()].
+list_gen(Size, ElemType) ->
+ Len = proper_arith:rand_int(0, Size),
+ vector_gen(Len, ElemType).
+
+%% @private
+-spec distlist_gen(size(), sized_generator(), boolean()) -> [imm_instance()].
+distlist_gen(RawSize, Gen, NonEmpty) ->
+ Len = case NonEmpty of
+ true -> proper_arith:rand_int(1, erlang:max(1,RawSize));
+ false -> proper_arith:rand_int(0, RawSize)
+ end,
+ Size = case Len of
+ 1 -> RawSize - 1;
+ _ -> RawSize
+ end,
+ %% TODO: this produces a lot of types: maybe a simple 'div' is sufficient?
+ Sizes = proper_arith:distribute(Size, Len),
+ InnerTypes = [Gen(S) || S <- Sizes],
+ fixed_list_gen(InnerTypes).
+
+%% @private
+-spec vector_gen(length(), proper_types:type()) -> [imm_instance()].
+vector_gen(Len, ElemType) ->
+ vector_gen_tr(Len, ElemType, []).
+
+-spec vector_gen_tr(length(), proper_types:type(), [imm_instance()]) ->
+ [imm_instance()].
+vector_gen_tr(0, _ElemType, AccList) ->
+ AccList;
+vector_gen_tr(Left, ElemType, AccList) ->
+ vector_gen_tr(Left - 1, ElemType, [generate(ElemType) | AccList]).
+
+%% @private
+-spec union_gen([proper_types:type(),...]) -> imm_instance().
+union_gen(Choices) ->
+ {_Choice,Type} = proper_arith:rand_choose(Choices),
+ generate(Type).
+
+%% @private
+-spec weighted_union_gen([{frequency(),proper_types:type()},...]) ->
+ imm_instance().
+weighted_union_gen(FreqChoices) ->
+ {_Choice,Type} = proper_arith:freq_choose(FreqChoices),
+ generate(Type).
+
+%% @private
+-spec safe_union_gen([proper_types:type(),...]) -> imm_instance().
+safe_union_gen(Choices) ->
+ {Choice,Type} = proper_arith:rand_choose(Choices),
+ try generate(Type)
+ catch
+ error:_ ->
+ safe_union_gen(proper_arith:list_remove(Choice, Choices))
+ end.
+
+%% @private
+-spec safe_weighted_union_gen([{frequency(),proper_types:type()},...]) ->
+ imm_instance().
+safe_weighted_union_gen(FreqChoices) ->
+ {Choice,Type} = proper_arith:freq_choose(FreqChoices),
+ try generate(Type)
+ catch
+ error:_ ->
+ safe_weighted_union_gen(proper_arith:list_remove(Choice,
+ FreqChoices))
+ end.
+
+%% @private
+-spec tuple_gen([proper_types:type()]) -> tuple().
+tuple_gen(Fields) ->
+ list_to_tuple(fixed_list_gen(Fields)).
+
+%% @private
+-spec loose_tuple_gen(size(), proper_types:type()) -> proper_types:type().
+loose_tuple_gen(Size, ElemType) ->
+ ?LET(L,
+ proper_types:resize(Size, proper_types:list(ElemType)),
+ list_to_tuple(L)).
+
+%% @private
+-spec loose_tuple_rev(tuple(), proper_types:type()) -> imm_instance().
+loose_tuple_rev(Tuple, ElemType) ->
+ CleanList = tuple_to_list(Tuple),
+ List = case proper_types:find_prop(reverse_gen, ElemType) of
+ {ok,{typed, ReverseGen}} ->
+ [ReverseGen(ElemType,X) || X <- CleanList];
+ {ok,ReverseGen} -> [ReverseGen(X) || X <- CleanList];
+ error -> CleanList
+ end,
+ {'$used', List, Tuple}.
+
+%% @private
+-spec exactly_gen(T) -> T.
+exactly_gen(X) ->
+ X.
+
+%% @private
+-spec fixed_list_gen([proper_types:type()]) -> imm_instance()
+ ; ({[proper_types:type()],proper_types:type()}) ->
+ maybe_improper_list(imm_instance(), imm_instance() | []).
+fixed_list_gen({ProperHead,ImproperTail}) ->
+ [generate(F) || F <- ProperHead] ++ generate(ImproperTail);
+fixed_list_gen(ProperFields) ->
+ [generate(F) || F <- ProperFields].
+
+%% @private
+-spec function_gen(arity(), proper_types:type()) -> function().
+function_gen(Arity, RetType) ->
+ FunSeed = {proper_arith:rand_int(0, ?SEED_RANGE - 1),
+ proper_arith:rand_int(0, ?SEED_RANGE - 1)},
+ create_fun(Arity, RetType, FunSeed).
+
+%% @private
+-spec any_gen(size()) -> imm_instance().
+any_gen(Size) ->
+ case get('$any_type') of
+ undefined -> real_any_gen(Size);
+ {type,AnyType} -> generate(proper_types:resize(Size, AnyType))
+ end.
+
+-spec real_any_gen(size()) -> imm_instance().
+real_any_gen(0) ->
+ SimpleTypes = [proper_types:integer(), proper_types:float(),
+ proper_types:atom()],
+ union_gen(SimpleTypes);
+real_any_gen(Size) ->
+ FreqChoices = [{?ANY_SIMPLE_PROB,simple}, {?ANY_BINARY_PROB,binary},
+ {?ANY_EXPAND_PROB,expand}],
+ case proper_arith:freq_choose(FreqChoices) of
+ {_,simple} ->
+ real_any_gen(0);
+ {_,binary} ->
+ generate(proper_types:resize(Size, proper_types:bitstring()));
+ {_,expand} ->
+ %% TODO: statistics of produced terms?
+ NumElems = proper_arith:rand_int(0, Size - 1),
+ ElemSizes = proper_arith:distribute(Size - 1, NumElems),
+ ElemTypes = [?LAZY(real_any_gen(S)) || S <- ElemSizes],
+ case proper_arith:rand_int(1,2) of
+ 1 -> fixed_list_gen(ElemTypes);
+ 2 -> tuple_gen(ElemTypes)
+ end
+ end.
+
+%% @private
+-spec native_type_gen(mod_name(), string()) -> proper_types:type().
+native_type_gen(Mod, TypeStr) ->
+ case proper_typeserver:translate_type({Mod,TypeStr}) of
+ {ok,Type} -> Type;
+ {error,Reason} -> throw({'$typeserver',Reason})
+ end.
+
+
+%%------------------------------------------------------------------------------
+%% Function-generation functions
+%%------------------------------------------------------------------------------
+
+-spec create_fun(arity(), proper_types:type(), fun_seed()) -> function().
+create_fun(_Arity, _RetType, _FunSeed) ->
+ fun() -> throw('$arity_limit') end.
+
+%% @private
+-spec get_ret_type(function()) -> proper_types:type().
+get_ret_type(Fun) ->
+ {arity,Arity} = erlang:fun_info(Fun, arity),
+ put('$get_ret_type', true),
+ RetType = apply(Fun, lists:duplicate(Arity,dummy)),
+ erase('$get_ret_type'),
+ RetType.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_internal.hrl b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_internal.hrl
new file mode 100644
index 0000000000..89e6b34296
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_internal.hrl
@@ -0,0 +1,98 @@
+%%% Copyright 2010-2013 Manolis Papadakis <[email protected]>,
+%%% Eirini Arvaniti <[email protected]>
+%%% and Kostis Sagonas <[email protected]>
+%%%
+%%% This file is part of PropEr.
+%%%
+%%% PropEr is free software: you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation, either version 3 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% PropEr is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with PropEr. If not, see <http://www.gnu.org/licenses/>.
+
+%%% @copyright 2010-2016 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
+%%% @version {@version}
+%%% @author Manolis Papadakis
+%%% @doc Internal header file: This header is included in all PropEr source
+%%% files.
+
+-include("compile_flags.hrl").
+-include("proper_common.hrl").
+
+
+%%------------------------------------------------------------------------------
+%% Activate strip_types parse transform
+%%------------------------------------------------------------------------------
+
+-ifdef(NO_TYPES).
+-compile({parse_transform, strip_types}).
+-endif.
+
+%%------------------------------------------------------------------------------
+%% Random generator selection
+%%------------------------------------------------------------------------------
+
+-ifdef(USE_SFMT).
+-define(RANDOM_MOD, sfmt).
+-define(SEED_NAME, sfmt_seed).
+-else.
+-define(RANDOM_MOD, random).
+-define(SEED_NAME, random_seed).
+-endif.
+
+%%------------------------------------------------------------------------------
+%% Macros
+%%------------------------------------------------------------------------------
+
+-define(PROPERTY_PREFIX, "prop_").
+
+
+%%------------------------------------------------------------------------------
+%% Constants
+%%------------------------------------------------------------------------------
+
+-define(SEED_RANGE, 4294967296).
+-define(MAX_ARITY, 20).
+-define(MAX_TRIES_FACTOR, 5).
+-define(ANY_SIMPLE_PROB, 3).
+-define(ANY_BINARY_PROB, 1).
+-define(ANY_EXPAND_PROB, 8).
+-define(SMALL_RANGE_THRESHOLD, 16#FFFF).
+
+
+%%------------------------------------------------------------------------------
+%% Common type aliases
+%%------------------------------------------------------------------------------
+
+%% TODO: Perhaps these should be moved inside modules.
+-type mod_name() :: atom().
+-type fun_name() :: atom().
+-type size() :: non_neg_integer().
+-type length() :: non_neg_integer().
+-type position() :: pos_integer().
+-type frequency() :: pos_integer().
+-type seed() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
+
+-type abs_form() :: erl_parse:abstract_form().
+-type abs_expr() :: erl_parse:abstract_expr().
+-type abs_clause() :: erl_parse:abstract_clause().
+
+%% TODO: Replace these with the appropriate types from stdlib.
+-ifdef(AT_LEAST_19).
+-type abs_type() :: erl_parse:abstract_type().
+-type abs_rec_field() :: term(). % erl_parse:af_field_decl().
+-else.
+-type abs_type() :: term().
+-type abs_rec_field() :: term().
+-endif.
+
+-type loose_tuple(T) :: {} | {T} | {T,T} | {T,T,T} | {T,T,T,T} | {T,T,T,T,T}
+ | {T,T,T,T,T,T} | {T,T,T,T,T,T,T} | {T,T,T,T,T,T,T,T}
+ | {T,T,T,T,T,T,T,T,T} | {T,T,T,T,T,T,T,T,T,T} | tuple().
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_types.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_types.erl
new file mode 100644
index 0000000000..6b154b813b
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_types.erl
@@ -0,0 +1,1353 @@
+%%% Copyright 2010-2013 Manolis Papadakis <[email protected]>,
+%%% Eirini Arvaniti <[email protected]>
+%%% and Kostis Sagonas <[email protected]>
+%%%
+%%% This file is part of PropEr.
+%%%
+%%% PropEr is free software: you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation, either version 3 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% PropEr is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with PropEr. If not, see <http://www.gnu.org/licenses/>.
+
+%%% @copyright 2010-2013 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
+%%% @version {@version}
+%%% @author Manolis Papadakis
+
+%%% @doc Type manipulation functions and predefined types.
+%%%
+%%% == Basic types ==
+%%% This module defines all the basic types of the PropEr type system as
+%%% functions. See the <a href="#index">function index</a> for an overview.
+%%%
+%%% Types can be combined in tuples or lists to produce other types. Exact
+%%% values (such as exact numbers, atoms, binaries and strings) can be combined
+%%% with types inside such structures, like in this example of the type of a
+%%% tagged tuple: ``{'result', integer()}''.
+%%%
+%%% When including the PropEr header file, all
+%%% <a href="#index">API functions</a> of this module are automatically
+%%% imported, unless `PROPER_NO_IMPORTS' is defined.
+%%%
+%%% == Customized types ==
+%%% The following operators can be applied to basic types in order to produce
+%%% new ones:
+%%%
+%%% <dl>
+%%% <dt>`?LET(<Xs>, <Xs_type>, <In>)'</dt>
+%%% <dd>To produce an instance of this type, all appearances of the variables
+%%% in `<Xs>' are replaced inside `<In>' by their corresponding values in a
+%%% randomly generated instance of `<Xs_type>'. It's OK for the `<In>' part to
+%%% evaluate to a type - in that case, an instance of the inner type is
+%%% generated recursively.</dd>
+%%% <dt>`?SUCHTHAT(<X>, <Type>, <Condition>)'</dt>
+%%% <dd>This produces a specialization of `<Type>', which only includes those
+%%% members of `<Type>' that satisfy the constraint `<Condition>' - that is,
+%%% those members for which the function `fun(<X>) -> <Condition> end' returns
+%%% `true'. If the constraint is very strict - that is, only a small
+%%% percentage of instances of `<Type>' pass the test - it will take a lot of
+%%% tries for the instance generation subsystem to randomly produce a valid
+%%% instance. This will result in slower testing, and testing may even be
+%%% stopped short, in case the `constraint_tries' limit is reached (see the
+%%% "Options" section in the documentation of the {@link proper} module). If
+%%% this is the case, it would be more appropriate to generate valid instances
+%%% of the specialized type using the `?LET' macro. Also make sure that even
+%%% small instances can satisfy the constraint, since PropEr will only try
+%%% small instances at the start of testing. If this is not possible, you can
+%%% instruct PropEr to start at a larger size, by supplying a suitable value
+%%% for the `start_size' option (see the "Options" section in the
+%%% documentation of the {@link proper} module).</dd>
+%%% <dt>`?SUCHTHATMAYBE(<X>, <Type>, <Condition>)'</dt>
+%%% <dd>Equivalent to the `?SUCHTHAT' macro, but the constraint `<Condition>'
+%%% is considered non-strict: if the `constraint_tries' limit is reached, the
+%%% generator will just return an instance of `<Type>' instead of failing,
+%%% even if that instance doesn't satisfy the constraint.</dd>
+%%% <dt>`?SHRINK(<Generator>, <List_of_alt_gens>)'</dt>
+%%% <dd>This creates a type whose instances are generated by evaluating the
+%%% statement block `<Generator>' (this may evaluate to a type, which will
+%%% then be generated recursively). If an instance of such a type is to be
+%%% shrunk, the generators in `<List_of_alt_gens>' are first run to produce
+%%% hopefully simpler instances of the type. Thus, the generators in the
+%%% second argument should be simpler than the default. The simplest ones
+%%% should be at the front of the list, since those are the generators
+%%% preferred by the shrinking subsystem. Like the main `<Generator>', the
+%%% alternatives may also evaluate to a type, which is generated recursively.
+%%% </dd>
+%%% <dt>`?LETSHRINK(<List_of_variables>, <List_of_types>, <Generator>)'</dt>
+%%% <dd>This is created by combining a `?LET' and a `?SHRINK' macro. Instances
+%%% are generated by applying a randomly generated list of values inside
+%%% `<Generator>' (just like a `?LET', with the added constraint that the
+%%% variables and types must be provided in a list - alternatively,
+%%% `<List_of_types>' may be a list or vector type). When shrinking instances
+%%% of such a type, the sub-instances that were combined to produce it are
+%%% first tried in place of the failing instance.</dd>
+%%% <dt>`?LAZY(<Generator>)'</dt>
+%%% <dd>This construct returns a type whose only purpose is to delay the
+%%% evaluation of `<Generator>' (`<Generator>' can return a type, which will
+%%% be generated recursively). Using this, you can simulate the lazy
+%%% generation of instances:
+%%% ``` stream() -> ?LAZY(frequency([ {1,[]}, {3,[0|stream()]} ])). '''
+%%% The above type produces lists of zeroes with an average length of 3. Note
+%%% that, had we not enclosed the generator with a `?LAZY' macro, the
+%%% evaluation would continue indefinitely, due to the eager evaluation of
+%%% the Erlang language.</dd>
+%%% <dt>`non_empty(<List_or_binary_type>)'</dt>
+%%% <dd>See the documentation for {@link non_empty/1}.</dd>
+%%% <dt>`noshrink(<Type>)'</dt>
+%%% <dd>See the documentation for {@link noshrink/1}.</dd>
+%%% <dt>`default(<Default_value>, <Type>)'</dt>
+%%% <dd>See the documentation for {@link default/2}.</dd>
+%%% <dt>`with_parameter(<Parameter>, <Value>, <Type>)'</dt>
+%%% <dd>See the documentation for {@link with_parameter/3}.</dd>
+%%% <dt>`with_parameters(<Param_value_pairs>, <Type>)'</dt>
+%%% <dd>See the documentation for {@link with_parameters/2}.</dd>
+%%% </dl>
+%%%
+%%% == Size manipulation ==
+%%% The following operators are related to the `size' parameter, which controls
+%%% the maximum size of produced instances. The actual size of a produced
+%%% instance is chosen randomly, but can never exceed the value of the `size'
+%%% parameter at the moment of generation. A more accurate definition is the
+%%% following: the maximum instance of `size S' can never be smaller than the
+%%% maximum instance of `size S-1'. The actual size of an instance is measured
+%%% differently for each type: the actual size of a list is its length, while
+%%% the actual size of a tree may be the number of its internal nodes. Some
+%%% types, e.g. unions, have no notion of size, thus their generation is not
+%%% influenced by the value of `size'. The `size' parameter starts at 1 and
+%%% grows automatically during testing.
+%%%
+%%% <dl>
+%%% <dt>`?SIZED(<S>, <Generator>)'</dt>
+%%% <dd>Creates a new type, whose instances are produced by replacing all
+%%% appearances of the `<S>' parameter inside the statement block
+%%% `<Generator>' with the value of the `size' parameter. It's OK for the
+%%% `<Generator>' to return a type - in that case, an instance of the inner
+%%% type is generated recursively.</dd>
+%%% <dt>`resize(<New_size>, <Type>)'</dt>
+%%% <dd>See the documentation for {@link resize/2}.</dd>
+%%% </dl>
+
+-module(proper_types).
+-export([is_inst/2, is_inst/3]).
+
+-export([integer/2, float/2, atom/0, binary/0, binary/1, bitstring/0,
+ bitstring/1, list/1, vector/2, union/1, weighted_union/1, tuple/1,
+ loose_tuple/1, exactly/1, fixed_list/1, function/2, any/0,
+ shrink_list/1, safe_union/1, safe_weighted_union/1]).
+-export([integer/0, non_neg_integer/0, pos_integer/0, neg_integer/0, range/2,
+ float/0, non_neg_float/0, number/0, boolean/0, byte/0, char/0,
+ list/0, tuple/0, string/0, wunion/1, term/0, timeout/0, arity/0]).
+-export([int/0, nat/0, largeint/0, real/0, bool/0, choose/2, elements/1,
+ oneof/1, frequency/1, return/1, default/2, orderedlist/1, function0/1,
+ function1/1, function2/1, function3/1, function4/1,
+ weighted_default/2]).
+-export([resize/2, non_empty/1, noshrink/1]).
+
+-export([cook_outer/1, is_type/1, equal_types/2, is_raw_type/1, to_binary/1,
+ from_binary/1, get_prop/2, find_prop/2, safe_is_instance/2,
+ is_instance/2, unwrap/1, weakly/1, strongly/1, satisfies_all/2,
+ new_type/2, subtype/2]).
+-export([lazy/1, sized/1, bind/3, shrinkwith/2, add_constraint/3,
+ native_type/2, distlist/3, with_parameter/3, with_parameters/2,
+ parameter/1, parameter/2]).
+-export([le/2]).
+
+-export_type([type/0, raw_type/0, extint/0, extnum/0]).
+
+-include("proper_internal.hrl").
+
+
+%%------------------------------------------------------------------------------
+%% Comparison with erl_types
+%%------------------------------------------------------------------------------
+
+%% Missing types
+%% -------------------
+%% will do:
+%% records, maybe_improper_list(T,S), nonempty_improper_list(T,S)
+%% maybe_improper_list(), maybe_improper_list(T), iolist, iodata
+%% don't need:
+%% nonempty_{list,string,maybe_improper_list}
+%% won't do:
+%% pid, port, ref, identifier, none, no_return, module, mfa, node
+%% array, dict, digraph, set, gb_tree, gb_set, queue, tid
+
+%% Missing type information
+%% ------------------------
+%% bin types:
+%% other unit sizes? what about size info?
+%% functions:
+%% generally some fun, unspecified number of arguments but specified
+%% return type
+%% any:
+%% doesn't cover functions and improper lists
+
+
+%%------------------------------------------------------------------------------
+%% Type declaration macros
+%%------------------------------------------------------------------------------
+
+-define(BASIC(PropList), new_type(PropList,basic)).
+-define(WRAPPER(PropList), new_type(PropList,wrapper)).
+-define(CONSTRUCTED(PropList), new_type(PropList,constructed)).
+-define(CONTAINER(PropList), new_type(PropList,container)).
+-define(SUBTYPE(Type,PropList), subtype(PropList,Type)).
+
+
+%%------------------------------------------------------------------------------
+%% Types
+%%------------------------------------------------------------------------------
+
+-type type_kind() :: 'basic' | 'wrapper' | 'constructed' | 'container' | atom().
+-type instance_test() :: fun((proper_gen:imm_instance()) -> boolean())
+ | {'typed',
+ fun((proper_types:type(),
+ proper_gen:imm_instance()) -> boolean())}.
+-type index() :: pos_integer().
+%% @alias
+-type value() :: term().
+%% @private_type
+%% @alias
+-type extint() :: integer() | 'inf'.
+%% @private_type
+%% @alias
+-type extnum() :: number() | 'inf'.
+-type constraint_fun() :: fun((proper_gen:instance()) -> boolean()).
+
+-opaque type() :: {'$type', [type_prop()]}.
+%% A type of the PropEr type system
+%% @type raw_type(). You can consider this as an equivalent of {@type type()}.
+-type raw_type() :: type() | [raw_type()] | loose_tuple(raw_type()) | term().
+-type type_prop_name() :: 'kind' | 'generator' | 'reverse_gen' | 'parts_type'
+ | 'combine' | 'alt_gens' | 'shrink_to_parts'
+ | 'size_transform' | 'is_instance' | 'shrinkers'
+ | 'noshrink' | 'internal_type' | 'internal_types'
+ | 'get_length' | 'split' | 'join' | 'get_indices'
+ | 'remove' | 'retrieve' | 'update' | 'constraints'
+ | 'parameters' | 'env' | 'subenv'.
+
+-type type_prop_value() :: term().
+-type type_prop() ::
+ {'kind', type_kind()}
+ | {'generator', proper_gen:generator()}
+ | {'reverse_gen', proper_gen:reverse_gen()}
+ | {'parts_type', type()}
+ | {'combine', proper_gen:combine_fun()}
+ | {'alt_gens', proper_gen:alt_gens()}
+ | {'shrink_to_parts', boolean()}
+ | {'size_transform', fun((size()) -> size())}
+ | {'is_instance', instance_test()}
+ | {'shrinkers', [proper_shrink:shrinker()]}
+ | {'noshrink', boolean()}
+ | {'internal_type', raw_type()}
+ | {'internal_types', tuple() | maybe_improper_list(type(),type() | [])}
+ %% The items returned by 'remove' must be of this type.
+ | {'get_length', fun((proper_gen:imm_instance()) -> length())}
+ %% If this is a container type, this should return the number of elements
+ %% it contains.
+ | {'split', fun((proper_gen:imm_instance()) -> [proper_gen:imm_instance()])
+ | fun((length(),proper_gen:imm_instance()) ->
+ {proper_gen:imm_instance(),proper_gen:imm_instance()})}
+ %% If present, the appropriate form depends on whether get_length is
+ %% defined: if get_length is undefined, this must be in the one-argument
+ %% form (e.g. a tree should be split into its subtrees), else it must be
+ %% in the two-argument form (e.g. a list should be split in two at the
+ %% index provided).
+ | {'join', fun((proper_gen:imm_instance(),proper_gen:imm_instance()) ->
+ proper_gen:imm_instance())}
+ | {'get_indices', fun((proper_types:type(),
+ proper_gen:imm_instance()) -> [index()])}
+ %% If this is a container type, this should return a list of indices we
+ %% can use to remove or insert elements from the given instance.
+ | {'remove', fun((index(),proper_gen:imm_instance()) ->
+ proper_gen:imm_instance())}
+ | {'retrieve', fun((index(), proper_gen:imm_instance() | tuple()
+ | maybe_improper_list(type(),type() | [])) ->
+ value() | type())}
+ | {'update', fun((index(),value(),proper_gen:imm_instance()) ->
+ proper_gen:imm_instance())}
+ | {'constraints', [{constraint_fun(), boolean()}]}
+ %% A list of constraints on instances of this type: each constraint is a
+ %% tuple of a fun that must return 'true' for each valid instance and a
+ %% boolean field that specifies whether the condition is strict.
+ | {'parameters', [{atom(),value()}]}
+ | {'env', term()}
+ | {'subenv', term()}.
+
+
+%%------------------------------------------------------------------------------
+%% Type manipulation functions
+%%------------------------------------------------------------------------------
+
+%% TODO: We shouldn't need the fully qualified type name in the range of these
+%% functions.
+
+%% @private
+%% TODO: just cook/1 ?
+-spec cook_outer(raw_type()) -> proper_types:type().
+cook_outer(Type = {'$type',_Props}) ->
+ Type;
+cook_outer(RawType) ->
+ if
+ is_tuple(RawType) -> tuple(tuple_to_list(RawType));
+ %% CAUTION: this must handle improper lists
+ is_list(RawType) -> fixed_list(RawType);
+ %% default case (covers integers, floats, atoms, binaries, ...):
+ true -> exactly(RawType)
+ end.
+
+%% @private
+-spec is_type(term()) -> boolean().
+is_type({'$type',_Props}) ->
+ true;
+is_type(_) ->
+ false.
+
+%% @private
+-spec equal_types(proper_types:type(), proper_types:type()) -> boolean().
+equal_types(SameType, SameType) ->
+ true;
+equal_types(_, _) ->
+ false.
+
+%% @private
+-spec is_raw_type(term()) -> boolean().
+is_raw_type({'$type',_TypeProps}) ->
+ true;
+is_raw_type(X) ->
+ if
+ is_tuple(X) -> is_raw_type_list(tuple_to_list(X));
+ is_list(X) -> is_raw_type_list(X);
+ true -> false
+ end.
+
+-spec is_raw_type_list(maybe_improper_list()) -> boolean().
+%% CAUTION: this must handle improper lists
+is_raw_type_list(List) ->
+ proper_arith:safe_any(fun is_raw_type/1, List).
+
+%% @private
+-spec to_binary(proper_types:type()) -> binary().
+to_binary(Type) ->
+ term_to_binary(Type).
+
+%% @private
+-ifdef(AT_LEAST_17).
+-spec from_binary(binary()) -> proper_types:type().
+-endif.
+from_binary(Binary) ->
+ binary_to_term(Binary).
+
+-spec type_from_list([type_prop()]) -> proper_types:type().
+type_from_list(KeyValueList) ->
+ {'$type',KeyValueList}.
+
+-spec add_prop(type_prop_name(), type_prop_value(), proper_types:type()) ->
+ proper_types:type().
+add_prop(PropName, Value, {'$type',Props}) ->
+ {'$type',lists:keystore(PropName, 1, Props, {PropName, Value})}.
+
+-spec add_props([type_prop()], proper_types:type()) -> proper_types:type().
+add_props(PropList, {'$type',OldProps}) ->
+ {'$type', lists:foldl(fun({N,_}=NV,Acc) ->
+ lists:keystore(N, 1, Acc, NV)
+ end, OldProps, PropList)}.
+
+-spec append_to_prop(type_prop_name(), type_prop_value(),
+ proper_types:type()) -> proper_types:type().
+append_to_prop(PropName, Value, {'$type',Props}) ->
+ Val = case lists:keyfind(PropName, 1, Props) of
+ {PropName, V} ->
+ V;
+ _ ->
+ []
+ end,
+ {'$type', lists:keystore(PropName, 1, Props,
+ {PropName, lists:reverse([Value|Val])})}.
+
+-spec append_list_to_prop(type_prop_name(), [type_prop_value()],
+ proper_types:type()) -> proper_types:type().
+append_list_to_prop(PropName, List, {'$type',Props}) ->
+ {PropName, Val} = lists:keyfind(PropName, 1, Props),
+ {'$type', lists:keystore(PropName, 1, Props, {PropName, Val++List})}.
+
+%% @private
+-spec get_prop(type_prop_name(), proper_types:type()) -> type_prop_value().
+get_prop(PropName, {'$type',Props}) ->
+ {_PropName, Val} = lists:keyfind(PropName, 1, Props),
+ Val.
+
+%% @private
+-spec find_prop(type_prop_name(), proper_types:type()) ->
+ {'ok',type_prop_value()} | 'error'.
+find_prop(PropName, {'$type',Props}) ->
+ case lists:keyfind(PropName, 1, Props) of
+ {PropName, Value} ->
+ {ok, Value};
+ _ ->
+ error
+ end.
+
+%% @private
+-spec new_type([type_prop()], type_kind()) -> proper_types:type().
+new_type(PropList, Kind) ->
+ Type = type_from_list(PropList),
+ add_prop(kind, Kind, Type).
+
+%% @private
+-spec subtype([type_prop()], proper_types:type()) -> proper_types:type().
+%% TODO: should the 'is_instance' function etc. be reset for subtypes?
+subtype(PropList, Type) ->
+ add_props(PropList, Type).
+
+%% @private
+-spec is_inst(proper_gen:instance(), raw_type()) ->
+ boolean() | {'error',{'typeserver',term()}}.
+is_inst(Instance, RawType) ->
+ is_inst(Instance, RawType, 10).
+
+%% @private
+-spec is_inst(proper_gen:instance(), raw_type(), size()) ->
+ boolean() | {'error',{'typeserver',term()}}.
+is_inst(Instance, RawType, Size) ->
+ proper:global_state_init_size(Size),
+ Result = safe_is_instance(Instance, RawType),
+ proper:global_state_erase(),
+ Result.
+
+%% @private
+-spec safe_is_instance(proper_gen:imm_instance(), raw_type()) ->
+ boolean() | {'error',{'typeserver',term()}}.
+safe_is_instance(ImmInstance, RawType) ->
+ try is_instance(ImmInstance, RawType) catch
+ throw:{'$typeserver',SubReason} -> {error, {typeserver,SubReason}}
+ end.
+
+%% @private
+-spec is_instance(proper_gen:imm_instance(), raw_type()) -> boolean().
+%% TODO: If the second argument is not a type, let it pass (don't even check for
+%% term equality?) - if it's a raw type, don't cook it, instead recurse
+%% into it.
+is_instance(ImmInstance, RawType) ->
+ CleanInstance = proper_gen:clean_instance(ImmInstance),
+ Type = cook_outer(RawType),
+ (case get_prop(kind, Type) of
+ wrapper -> wrapper_test(ImmInstance, Type);
+ constructed -> constructed_test(ImmInstance, Type);
+ _ -> false
+ end
+ orelse
+ case find_prop(is_instance, Type) of
+ {ok,{typed, IsInstance}} -> IsInstance(Type, ImmInstance);
+ {ok,IsInstance} -> IsInstance(ImmInstance);
+ error -> false
+ end)
+ andalso weakly(satisfies_all(CleanInstance, Type)).
+
+-spec wrapper_test(proper_gen:imm_instance(), proper_types:type()) -> boolean().
+wrapper_test(ImmInstance, Type) ->
+ %% TODO: check if it's actually a raw type that's returned?
+ lists:any(fun(T) -> is_instance(ImmInstance, T) end, unwrap(Type)).
+
+%% @private
+-ifdef(AT_LEAST_17).
+-spec unwrap(proper_types:type()) -> [proper_types:type(),...].
+-endif.
+%% TODO: check if it's actually a raw type that's returned?
+unwrap(Type) ->
+ RawInnerTypes = proper_gen:alt_gens(Type) ++ [proper_gen:normal_gen(Type)],
+ [cook_outer(T) || T <- RawInnerTypes].
+
+-spec constructed_test(proper_gen:imm_instance(), proper_types:type()) ->
+ boolean().
+constructed_test({'$used',ImmParts,ImmInstance}, Type) ->
+ PartsType = get_prop(parts_type, Type),
+ Combine = get_prop(combine, Type),
+ is_instance(ImmParts, PartsType) andalso
+ begin
+ %% TODO: check if it's actually a raw type that's returned?
+ %% TODO: move construction code to proper_gen
+ %% TODO: non-type => should we check for strict term equality?
+ RawInnerType = Combine(proper_gen:clean_instance(ImmParts)),
+ is_instance(ImmInstance, RawInnerType)
+ end;
+constructed_test({'$to_part',ImmInstance}, Type) ->
+ PartsType = get_prop(parts_type, Type),
+ get_prop(shrink_to_parts, Type) =:= true andalso
+ %% TODO: we reject non-container types
+ get_prop(kind, PartsType) =:= container andalso
+ case {find_prop(internal_type,PartsType),
+ find_prop(internal_types,PartsType)} of
+ {{ok,EachPartType},error} ->
+ %% The parts are in a list or a vector.
+ is_instance(ImmInstance, EachPartType);
+ {error,{ok,PartTypesList}} ->
+ %% The parts are in a fixed list.
+ %% TODO: It should always be a proper list.
+ lists:any(fun(T) -> is_instance(ImmInstance,T) end, PartTypesList)
+ end;
+constructed_test(_CleanInstance, _Type) ->
+ %% TODO: can we do anything better?
+ false.
+
+%% @private
+-spec weakly({boolean(),boolean()}) -> boolean().
+weakly({B1,_B2}) -> B1.
+
+%% @private
+-spec strongly({boolean(),boolean()}) -> boolean().
+strongly({_B1,B2}) -> B2.
+
+-spec satisfies(proper_gen:instance(), {constraint_fun(),boolean()})
+ -> {boolean(),boolean()}.
+satisfies(Instance, {Test,false}) ->
+ {true,Test(Instance)};
+satisfies(Instance, {Test,true}) ->
+ Result = Test(Instance),
+ {Result,Result}.
+
+%% @private
+-spec satisfies_all(proper_gen:instance(), proper_types:type()) ->
+ {boolean(),boolean()}.
+satisfies_all(Instance, Type) ->
+ case find_prop(constraints, Type) of
+ {ok, Constraints} ->
+ L = [satisfies(Instance, C) || C <- Constraints],
+ {L1,L2} = lists:unzip(L),
+ {lists:all(fun(B) -> B end, L1), lists:all(fun(B) -> B end, L2)};
+ error ->
+ {true,true}
+ end.
+
+
+%%------------------------------------------------------------------------------
+%% Type definition functions
+%%------------------------------------------------------------------------------
+
+%% @private
+-spec lazy(proper_gen:nosize_generator()) -> proper_types:type().
+lazy(Gen) ->
+ ?WRAPPER([
+ {generator, Gen}
+ ]).
+
+%% @private
+-spec sized(proper_gen:sized_generator()) -> proper_types:type().
+sized(Gen) ->
+ ?WRAPPER([
+ {generator, Gen}
+ ]).
+
+%% @private
+-spec bind(raw_type(), proper_gen:combine_fun(), boolean()) ->
+ proper_types:type().
+bind(RawPartsType, Combine, ShrinkToParts) ->
+ PartsType = cook_outer(RawPartsType),
+ ?CONSTRUCTED([
+ {parts_type, PartsType},
+ {combine, Combine},
+ {shrink_to_parts, ShrinkToParts}
+ ]).
+
+%% @private
+-spec shrinkwith(proper_gen:nosize_generator(), proper_gen:alt_gens()) ->
+ proper_types:type().
+shrinkwith(Gen, DelaydAltGens) ->
+ ?WRAPPER([
+ {generator, Gen},
+ {alt_gens, DelaydAltGens}
+ ]).
+
+%% @private
+-spec add_constraint(raw_type(), constraint_fun(), boolean()) ->
+ proper_types:type().
+add_constraint(RawType, Condition, IsStrict) ->
+ Type = cook_outer(RawType),
+ append_to_prop(constraints, {Condition,IsStrict}, Type).
+
+%% @private
+-spec native_type(mod_name(), string()) -> proper_types:type().
+native_type(Mod, TypeStr) ->
+ ?WRAPPER([
+ {generator, fun() -> proper_gen:native_type_gen(Mod,TypeStr) end}
+ ]).
+
+
+%%------------------------------------------------------------------------------
+%% Basic types
+%%------------------------------------------------------------------------------
+
+%% @doc All integers between `Low' and `High', bounds included.
+%% `Low' and `High' must be Erlang expressions that evaluate to integers, with
+%% `Low =< High'. Additionally, `Low' and `High' may have the value `inf', in
+%% which case they represent minus infinity and plus infinity respectively.
+%% Instances shrink towards 0 if `Low =< 0 =< High', or towards the bound with
+%% the smallest absolute value otherwise.
+-spec integer(extint(), extint()) -> proper_types:type().
+integer(Low, High) ->
+ ?BASIC([
+ {env, {Low, High}},
+ {generator, {typed, fun integer_gen/2}},
+ {is_instance, {typed, fun integer_is_instance/2}},
+ {shrinkers, [fun number_shrinker/3]}
+ ]).
+
+integer_gen(Type, Size) ->
+ {Low, High} = get_prop(env, Type),
+ proper_gen:integer_gen(Size, Low, High).
+
+integer_is_instance(Type, X) ->
+ {Low, High} = get_prop(env, Type),
+ is_integer(X) andalso le(Low, X) andalso le(X, High).
+
+number_shrinker(X, Type, S) ->
+ {Low, High} = get_prop(env, Type),
+ proper_shrink:number_shrinker(X, Low, High, S).
+
+%% @doc All floats between `Low' and `High', bounds included.
+%% `Low' and `High' must be Erlang expressions that evaluate to floats, with
+%% `Low =< High'. Additionally, `Low' and `High' may have the value `inf', in
+%% which case they represent minus infinity and plus infinity respectively.
+%% Instances shrink towards 0.0 if `Low =< 0.0 =< High', or towards the bound
+%% with the smallest absolute value otherwise.
+-spec float(extnum(), extnum()) -> proper_types:type().
+float(Low, High) ->
+ ?BASIC([
+ {env, {Low, High}},
+ {generator, {typed, fun float_gen/2}},
+ {is_instance, {typed, fun float_is_instance/2}},
+ {shrinkers, [fun number_shrinker/3]}
+ ]).
+
+float_gen(Type, Size) ->
+ {Low, High} = get_prop(env, Type),
+ proper_gen:float_gen(Size, Low, High).
+
+float_is_instance(Type, X) ->
+ {Low, High} = get_prop(env, Type),
+ is_float(X) andalso le(Low, X) andalso le(X, High).
+
+%% @private
+-spec le(extnum(), extnum()) -> boolean().
+le(inf, _B) -> true;
+le(_A, inf) -> true;
+le(A, B) -> A =< B.
+
+%% @doc All atoms. All atoms used internally by PropEr start with a '`$'', so
+%% such atoms will never be produced as instances of this type. You should also
+%% refrain from using such atoms in your code, to avoid a potential clash.
+%% Instances shrink towards the empty atom, ''.
+-spec atom() -> proper_types:type().
+atom() ->
+ ?WRAPPER([
+ {generator, fun proper_gen:atom_gen/1},
+ {reverse_gen, fun proper_gen:atom_rev/1},
+ {size_transform, fun(Size) -> erlang:min(Size,255) end},
+ {is_instance, fun atom_is_instance/1}
+ ]).
+
+atom_is_instance(X) ->
+ is_atom(X)
+ %% We return false for atoms starting with '$', since these are
+ %% atoms used internally and never produced by the atom generator.
+ andalso (X =:= '' orelse hd(atom_to_list(X)) =/= $$).
+
+%% @doc All binaries. Instances shrink towards the empty binary, `<<>>'.
+-spec binary() -> proper_types:type().
+binary() ->
+ ?WRAPPER([
+ {generator, fun proper_gen:binary_gen/1},
+ {reverse_gen, fun proper_gen:binary_rev/1},
+ {is_instance, fun erlang:is_binary/1}
+ ]).
+
+%% @doc All binaries with a byte size of `Len'.
+%% `Len' must be an Erlang expression that evaluates to a non-negative integer.
+%% Instances shrink towards binaries of zeroes.
+-spec binary(length()) -> proper_types:type().
+binary(Len) ->
+ ?WRAPPER([
+ {env, Len},
+ {generator, {typed, fun binary_len_gen/1}},
+ {reverse_gen, fun proper_gen:binary_rev/1},
+ {is_instance, {typed, fun binary_len_is_instance/2}}
+ ]).
+
+binary_len_gen(Type) ->
+ Len = get_prop(env, Type),
+ proper_gen:binary_len_gen(Len).
+
+binary_len_is_instance(Type, X) ->
+ Len = get_prop(env, Type),
+ is_binary(X) andalso byte_size(X) =:= Len.
+
+%% @doc All bitstrings. Instances shrink towards the empty bitstring, `<<>>'.
+-spec bitstring() -> proper_types:type().
+bitstring() ->
+ ?WRAPPER([
+ {generator, fun proper_gen:bitstring_gen/1},
+ {reverse_gen, fun proper_gen:bitstring_rev/1},
+ {is_instance, fun erlang:is_bitstring/1}
+ ]).
+
+%% @doc All bitstrings with a bit size of `Len'.
+%% `Len' must be an Erlang expression that evaluates to a non-negative integer.
+%% Instances shrink towards bitstrings of zeroes
+-spec bitstring(length()) -> proper_types:type().
+bitstring(Len) ->
+ ?WRAPPER([
+ {env, Len},
+ {generator, {typed, fun bitstring_len_gen/1}},
+ {reverse_gen, fun proper_gen:bitstring_rev/1},
+ {is_instance, {typed, fun bitstring_len_is_instance/2}}
+ ]).
+
+bitstring_len_gen(Type) ->
+ Len = get_prop(env, Type),
+ proper_gen:bitstring_len_gen(Len).
+
+bitstring_len_is_instance(Type, X) ->
+ Len = get_prop(env, Type),
+ is_bitstring(X) andalso bit_size(X) =:= Len.
+
+%% @doc All lists containing elements of type `ElemType'.
+%% Instances shrink towards the empty list, `[]'.
+-spec list(ElemType::raw_type()) -> proper_types:type().
+% TODO: subtyping would be useful here (list, vector, fixed_list)
+list(RawElemType) ->
+ ElemType = cook_outer(RawElemType),
+ ?CONTAINER([
+ {generator, {typed, fun list_gen/2}},
+ {is_instance, {typed, fun list_is_instance/2}},
+ {internal_type, ElemType},
+ {get_length, fun erlang:length/1},
+ {split, fun lists:split/2},
+ {join, fun lists:append/2},
+ {get_indices, fun list_get_indices/2},
+ {remove, fun proper_arith:list_remove/2},
+ {retrieve, fun lists:nth/2},
+ {update, fun proper_arith:list_update/3}
+ ]).
+
+list_gen(Type, Size) ->
+ ElemType = get_prop(internal_type, Type),
+ proper_gen:list_gen(Size, ElemType).
+
+list_is_instance(Type, X) ->
+ ElemType = get_prop(internal_type, Type),
+ list_test(X, ElemType).
+
+%% @doc A type that generates exactly the list `List'. Instances shrink towards
+%% shorter sublists of the original list.
+-spec shrink_list([term()]) -> proper_types:type().
+shrink_list(List) ->
+ ?CONTAINER([
+ {env, List},
+ {generator, {typed, fun shrink_list_gen/1}},
+ {is_instance, {typed, fun shrink_list_is_instance/2}},
+ {get_length, fun erlang:length/1},
+ {split, fun lists:split/2},
+ {join, fun lists:append/2},
+ {get_indices, fun list_get_indices/2},
+ {remove, fun proper_arith:list_remove/2}
+ ]).
+
+shrink_list_gen(Type) ->
+ get_prop(env, Type).
+
+shrink_list_is_instance(Type, X) ->
+ List = get_prop(env, Type),
+ is_sublist(X, List).
+
+-spec is_sublist([term()], [term()]) -> boolean().
+is_sublist([], _) -> true;
+is_sublist(_, []) -> false;
+is_sublist([H|T1], [H|T2]) -> is_sublist(T1, T2);
+is_sublist(Slice, [_|T2]) -> is_sublist(Slice, T2).
+
+-spec list_test(proper_gen:imm_instance(), proper_types:type()) -> boolean().
+list_test(X, ElemType) ->
+ is_list(X) andalso lists:all(fun(E) -> is_instance(E, ElemType) end, X).
+
+%% @private
+-spec list_get_indices(proper_gen:generator(), list()) -> [position()].
+list_get_indices(_, List) ->
+ lists:seq(1, length(List)).
+
+%% @private
+%% This assumes that:
+%% - instances of size S are always valid instances of size >S
+%% - any recursive calls inside Gen are lazy
+-spec distlist(size(), proper_gen:sized_generator(), boolean()) ->
+ proper_types:type().
+distlist(Size, Gen, NonEmpty) ->
+ ParentType = case NonEmpty of
+ true -> non_empty(list(Gen(Size)));
+ false -> list(Gen(Size))
+ end,
+ ?SUBTYPE(ParentType, [
+ {subenv, {Size, Gen, NonEmpty}},
+ {generator, {typed, fun distlist_gen/1}}
+ ]).
+
+distlist_gen(Type) ->
+ {Size, Gen, NonEmpty} = get_prop(subenv, Type),
+ proper_gen:distlist_gen(Size, Gen, NonEmpty).
+
+%% @doc All lists of length `Len' containing elements of type `ElemType'.
+%% `Len' must be an Erlang expression that evaluates to a non-negative integer.
+-spec vector(length(), ElemType::raw_type()) -> proper_types:type().
+vector(Len, RawElemType) ->
+ ElemType = cook_outer(RawElemType),
+ ?CONTAINER([
+ {env, Len},
+ {generator, {typed, fun vector_gen/1}},
+ {is_instance, {typed, fun vector_is_instance/2}},
+ {internal_type, ElemType},
+ {get_indices, fun vector_get_indices/2},
+ {retrieve, fun lists:nth/2},
+ {update, fun proper_arith:list_update/3}
+ ]).
+
+vector_gen(Type) ->
+ Len = get_prop(env, Type),
+ ElemType = get_prop(internal_type, Type),
+ proper_gen:vector_gen(Len, ElemType).
+
+vector_is_instance(Type, X) ->
+ Len = get_prop(env, Type),
+ ElemType = get_prop(internal_type, Type),
+ is_list(X)
+ andalso length(X) =:= Len
+ andalso lists:all(fun(E) -> is_instance(E, ElemType) end, X).
+
+vector_get_indices(Type, _X) ->
+ lists:seq(1, get_prop(env, Type)).
+
+%% @doc The union of all types in `ListOfTypes'. `ListOfTypes' can't be empty.
+%% The random instance generator is equally likely to choose any one of the
+%% types in `ListOfTypes'. The shrinking subsystem will always try to shrink an
+%% instance of a type union to an instance of the first type in `ListOfTypes',
+%% thus you should write the simplest case first.
+-spec union(ListOfTypes::[raw_type(),...]) -> proper_types:type().
+union(RawChoices) ->
+ Choices = [cook_outer(C) || C <- RawChoices],
+ ?BASIC([
+ {env, Choices},
+ {generator, {typed, fun union_gen/1}},
+ {is_instance, {typed, fun union_is_instance/2}},
+ {shrinkers, [fun union_shrinker_1/3, fun union_shrinker_2/3]}
+ ]).
+
+union_gen(Type) ->
+ Choices = get_prop(env,Type),
+ proper_gen:union_gen(Choices).
+
+union_is_instance(Type, X) ->
+ Choices = get_prop(env, Type),
+ lists:any(fun(C) -> is_instance(X, C) end, Choices).
+
+union_shrinker_1(X, Type, S) ->
+ Choices = get_prop(env, Type),
+ proper_shrink:union_first_choice_shrinker(X, Choices, S).
+
+union_shrinker_2(X, Type, S) ->
+ Choices = get_prop(env, Type),
+ proper_shrink:union_recursive_shrinker(X, Choices, S).
+
+%% @doc A specialization of {@link union/1}, where each type in `ListOfTypes' is
+%% assigned a frequency. Frequencies must be Erlang expressions that evaluate to
+%% positive integers. Types with larger frequencies are more likely to be chosen
+%% by the random instance generator. The shrinking subsystem will ignore the
+%% frequencies and try to shrink towards the first type in the list.
+-spec weighted_union(ListOfTypes::[{frequency(),raw_type()},...]) ->
+ proper_types:type().
+weighted_union(RawFreqChoices) ->
+ CookFreqType = fun({Freq,RawType}) -> {Freq,cook_outer(RawType)} end,
+ FreqChoices = lists:map(CookFreqType, RawFreqChoices),
+ Choices = [T || {_F,T} <- FreqChoices],
+ ?SUBTYPE(union(Choices), [
+ {subenv, FreqChoices},
+ {generator, {typed, fun weighted_union_gen/1}}
+ ]).
+
+weighted_union_gen(Gen) ->
+ FreqChoices = get_prop(subenv, Gen),
+ proper_gen:weighted_union_gen(FreqChoices).
+
+%% @private
+-spec safe_union([raw_type(),...]) -> proper_types:type().
+safe_union(RawChoices) ->
+ Choices = [cook_outer(C) || C <- RawChoices],
+ subtype(
+ [{subenv, Choices},
+ {generator, {typed, fun safe_union_gen/1}}],
+ union(Choices)).
+
+safe_union_gen(Type) ->
+ Choices = get_prop(subenv, Type),
+ proper_gen:safe_union_gen(Choices).
+
+%% @private
+-spec safe_weighted_union([{frequency(),raw_type()},...]) ->
+ proper_types:type().
+safe_weighted_union(RawFreqChoices) ->
+ CookFreqType = fun({Freq,RawType}) ->
+ {Freq,cook_outer(RawType)} end,
+ FreqChoices = lists:map(CookFreqType, RawFreqChoices),
+ Choices = [T || {_F,T} <- FreqChoices],
+ subtype([{subenv, FreqChoices},
+ {generator, {typed, fun safe_weighted_union_gen/1}}],
+ union(Choices)).
+
+safe_weighted_union_gen(Type) ->
+ FreqChoices = get_prop(subenv, Type),
+ proper_gen:safe_weighted_union_gen(FreqChoices).
+
+%% @doc All tuples whose i-th element is an instance of the type at index i of
+%% `ListOfTypes'. Also written simply as a tuple of types.
+-spec tuple(ListOfTypes::[raw_type()]) -> proper_types:type().
+tuple(RawFields) ->
+ Fields = [cook_outer(F) || F <- RawFields],
+ ?CONTAINER([
+ {env, Fields},
+ {generator, {typed, fun tuple_gen/1}},
+ {is_instance, {typed, fun tuple_is_instance/2}},
+ {internal_types, list_to_tuple(Fields)},
+ {get_indices, fun tuple_get_indices/2},
+ {retrieve, fun erlang:element/2},
+ {update, fun tuple_update/3}
+ ]).
+
+tuple_gen(Type) ->
+ Fields = get_prop(env, Type),
+ proper_gen:tuple_gen(Fields).
+
+tuple_is_instance(Type, X) ->
+ Fields = get_prop(env, Type),
+ is_tuple(X) andalso fixed_list_test(tuple_to_list(X), Fields).
+
+tuple_get_indices(Type, _X) ->
+ lists:seq(1, length(get_prop(env, Type))).
+
+-spec tuple_update(index(), value(), tuple()) -> tuple().
+tuple_update(Index, NewElem, Tuple) ->
+ setelement(Index, Tuple, NewElem).
+
+%% @doc Tuples whose elements are all of type `ElemType'.
+%% Instances shrink towards the 0-size tuple, `{}'.
+-spec loose_tuple(ElemType::raw_type()) -> proper_types:type().
+loose_tuple(RawElemType) ->
+ ElemType = cook_outer(RawElemType),
+ ?WRAPPER([
+ {env, ElemType},
+ {generator, {typed, fun loose_tuple_gen/2}},
+ {reverse_gen, {typed, fun loose_tuple_rev/2}},
+ {is_instance, {typed, fun loose_tuple_is_instance/2}}
+ ]).
+
+loose_tuple_gen(Type, Size) ->
+ ElemType = get_prop(env, Type),
+ proper_gen:loose_tuple_gen(Size, ElemType).
+
+loose_tuple_rev(Type, X) ->
+ ElemType = get_prop(env, Type),
+ proper_gen:loose_tuple_rev(X, ElemType).
+
+loose_tuple_is_instance(Type, X) ->
+ ElemType = get_prop(env, Type),
+ is_tuple(X) andalso list_test(tuple_to_list(X), ElemType).
+
+%% @doc Singleton type consisting only of `E'. `E' must be an evaluated term.
+%% Also written simply as `E'.
+-spec exactly(term()) -> proper_types:type().
+exactly(E) ->
+ ?BASIC([
+ {env, E},
+ {generator, {typed, fun exactly_gen/1}},
+ {is_instance, {typed, fun exactly_is_instance/2}}
+ ]).
+
+exactly_gen(Type) ->
+ E = get_prop(env, Type),
+ proper_gen:exactly_gen(E).
+
+exactly_is_instance(Type, X) ->
+ E = get_prop(env, Type),
+ X =:= E.
+
+%% @doc All lists whose i-th element is an instance of the type at index i of
+%% `ListOfTypes'. Also written simply as a list of types.
+-spec fixed_list(ListOfTypes::maybe_improper_list(raw_type(),raw_type()|[])) ->
+ proper_types:type().
+fixed_list(MaybeImproperRawFields) ->
+ %% CAUTION: must handle improper lists
+ {Fields, Internal, Len, Retrieve, Update} =
+ case proper_arith:cut_improper_tail(MaybeImproperRawFields) of
+ % TODO: have cut_improper_tail return the length and use it in test?
+ {ProperRawHead, ImproperRawTail} ->
+ HeadLen = length(ProperRawHead),
+ CookedHead = [cook_outer(F) || F <- ProperRawHead],
+ CookedTail = cook_outer(ImproperRawTail),
+ {{CookedHead,CookedTail},
+ CookedHead ++ CookedTail,
+ HeadLen + 1,
+ fun(I,L) -> improper_list_retrieve(I, L, HeadLen) end,
+ fun(I,V,L) -> improper_list_update(I, V, L, HeadLen) end};
+ ProperRawFields ->
+ LocalFields = [cook_outer(F) || F <- ProperRawFields],
+ {LocalFields,
+ LocalFields,
+ length(ProperRawFields),
+ fun lists:nth/2,
+ fun proper_arith:list_update/3}
+ end,
+ ?CONTAINER([
+ {env, {Fields, Len}},
+ {generator, {typed, fun fixed_list_gen/1}},
+ {is_instance, {typed, fun fixed_list_is_instance/2}},
+ {internal_types, Internal},
+ {get_indices, fun fixed_list_get_indices/2},
+ {retrieve, Retrieve},
+ {update, Update}
+ ]).
+
+fixed_list_gen(Type) ->
+ {Fields, _} = get_prop(env, Type),
+ proper_gen:fixed_list_gen(Fields).
+
+fixed_list_is_instance(Type, X) ->
+ {Fields, _} = get_prop(env, Type),
+ fixed_list_test(X, Fields).
+
+fixed_list_get_indices(Type, _X) ->
+ {_, Len} = get_prop(env, Type),
+ lists:seq(1, Len).
+
+-spec fixed_list_test(proper_gen:imm_instance(),
+ [proper_types:type()] | {[proper_types:type()],
+ proper_types:type()}) ->
+ boolean().
+fixed_list_test(X, {ProperHead,ImproperTail}) ->
+ is_list(X) andalso
+ begin
+ ProperHeadLen = length(ProperHead),
+ proper_arith:head_length(X) >= ProperHeadLen andalso
+ begin
+ {XHead,XTail} = lists:split(ProperHeadLen, X),
+ fixed_list_test(XHead, ProperHead)
+ andalso is_instance(XTail, ImproperTail)
+ end
+ end;
+fixed_list_test(X, ProperFields) ->
+ is_list(X)
+ andalso length(X) =:= length(ProperFields)
+ andalso lists:all(fun({E,T}) -> is_instance(E, T) end,
+ lists:zip(X, ProperFields)).
+
+%% TODO: Move these 2 functions to proper_arith?
+-spec improper_list_retrieve(index(), nonempty_improper_list(value(),value()),
+ pos_integer()) -> value().
+improper_list_retrieve(Index, List, HeadLen) ->
+ case Index =< HeadLen of
+ true -> lists:nth(Index, List);
+ false -> lists:nthtail(HeadLen, List)
+ end.
+
+-spec improper_list_update(index(), value(),
+ nonempty_improper_list(value(),value()),
+ pos_integer()) ->
+ nonempty_improper_list(value(),value()).
+improper_list_update(Index, Value, List, HeadLen) ->
+ case Index =< HeadLen of
+ %% TODO: This happens to work, but is not implied by list_update's spec.
+ true -> proper_arith:list_update(Index, Value, List);
+ false -> lists:sublist(List, HeadLen) ++ Value
+ end.
+
+%% @doc All pure functions that map instances of `ArgTypes' to instances of
+%% `RetType'. The syntax `function(Arity, RetType)' is also acceptable.
+-spec function(ArgTypes::[raw_type()] | arity(), RetType::raw_type()) ->
+ proper_types:type().
+function(Arity, RawRetType) when is_integer(Arity), Arity >= 0, Arity =< 255 ->
+ RetType = cook_outer(RawRetType),
+ ?BASIC([
+ {env, {Arity, RetType}},
+ {generator, {typed, fun function_gen/1}},
+ {is_instance, {typed, fun function_is_instance/2}}
+ ]);
+function(RawArgTypes, RawRetType) ->
+ function(length(RawArgTypes), RawRetType).
+
+function_gen(Type) ->
+ {Arity, RetType} = get_prop(env, Type),
+ proper_gen:function_gen(Arity, RetType).
+
+function_is_instance(Type, X) ->
+ {Arity, RetType} = get_prop(env, Type),
+ is_function(X, Arity)
+ %% TODO: what if it's not a function we produced?
+ andalso equal_types(RetType, proper_gen:get_ret_type(X)).
+
+%% @doc All Erlang terms (that PropEr can produce). For reasons of efficiency,
+%% functions are never produced as instances of this type.<br />
+%% CAUTION: Instances of this type are expensive to produce, shrink and instance-
+%% check, both in terms of processing time and consumed memory. Only use this
+%% type if you are certain that you need it.
+-spec any() -> proper_types:type().
+any() ->
+ AllTypes = [integer(),float(),atom(),bitstring(),?LAZY(loose_tuple(any())),
+ ?LAZY(list(any()))],
+ ?SUBTYPE(union(AllTypes), [
+ {generator, fun proper_gen:any_gen/1}
+ ]).
+
+
+%%------------------------------------------------------------------------------
+%% Type aliases
+%%------------------------------------------------------------------------------
+
+%% @equiv integer(inf, inf)
+-spec integer() -> proper_types:type().
+integer() -> integer(inf, inf).
+
+%% @equiv integer(0, inf)
+-spec non_neg_integer() -> proper_types:type().
+non_neg_integer() -> integer(0, inf).
+
+%% @equiv integer(1, inf)
+-spec pos_integer() -> proper_types:type().
+pos_integer() -> integer(1, inf).
+
+%% @equiv integer(inf, -1)
+-spec neg_integer() -> proper_types:type().
+neg_integer() -> integer(inf, -1).
+
+%% @equiv integer(Low, High)
+-spec range(extint(), extint()) -> proper_types:type().
+range(Low, High) -> integer(Low, High).
+
+%% @equiv float(inf, inf)
+-spec float() -> proper_types:type().
+float() -> float(inf, inf).
+
+%% @equiv float(0.0, inf)
+-spec non_neg_float() -> proper_types:type().
+non_neg_float() -> float(0.0, inf).
+
+%% @equiv union([integer(), float()])
+-spec number() -> proper_types:type().
+number() -> union([integer(), float()]).
+
+%% @doc The atoms `true' and `false'. Instances shrink towards `false'.
+-spec boolean() -> proper_types:type().
+boolean() -> union(['false', 'true']).
+
+%% @equiv integer(0, 255)
+-spec byte() -> proper_types:type().
+byte() -> integer(0, 255).
+
+%% @equiv integer(0, 16#10ffff)
+-spec char() -> proper_types:type().
+char() -> integer(0, 16#10ffff).
+
+%% @equiv list(any())
+-spec list() -> proper_types:type().
+list() -> list(any()).
+
+%% @equiv loose_tuple(any())
+-spec tuple() -> proper_types:type().
+tuple() -> loose_tuple(any()).
+
+%% @equiv list(char())
+-spec string() -> proper_types:type().
+string() -> list(char()).
+
+%% @equiv weighted_union(FreqChoices)
+-spec wunion([{frequency(),raw_type()},...]) -> proper_types:type().
+wunion(FreqChoices) -> weighted_union(FreqChoices).
+
+%% @equiv any()
+-spec term() -> proper_types:type().
+term() -> any().
+
+%% @equiv union([non_neg_integer() | infinity])
+-spec timeout() -> proper_types:type().
+timeout() -> union([non_neg_integer(), 'infinity']).
+
+%% @equiv integer(0, 255)
+-spec arity() -> proper_types:type().
+arity() -> integer(0, 255).
+
+
+%%------------------------------------------------------------------------------
+%% QuickCheck compatibility types
+%%------------------------------------------------------------------------------
+
+%% @doc Small integers (bound by the current value of the `size' parameter).
+%% Instances shrink towards `0'.
+-spec int() -> proper_types:type().
+int() -> ?SIZED(Size, integer(-Size,Size)).
+
+%% @doc Small non-negative integers (bound by the current value of the `size'
+%% parameter). Instances shrink towards `0'.
+-spec nat() -> proper_types:type().
+nat() -> ?SIZED(Size, integer(0,Size)).
+
+%% @equiv integer()
+-spec largeint() -> proper_types:type().
+largeint() -> integer().
+
+%% @equiv float()
+-spec real() -> proper_types:type().
+real() -> float().
+
+%% @equiv boolean()
+-spec bool() -> proper_types:type().
+bool() -> boolean().
+
+%% @equiv integer(Low, High)
+-spec choose(extint(), extint()) -> proper_types:type().
+choose(Low, High) -> integer(Low, High).
+
+%% @equiv union(Choices)
+-spec elements([raw_type(),...]) -> proper_types:type().
+elements(Choices) -> union(Choices).
+
+%% @equiv union(Choices)
+-spec oneof([raw_type(),...]) -> proper_types:type().
+oneof(Choices) -> union(Choices).
+
+%% @equiv weighted_union(Choices)
+-spec frequency([{frequency(),raw_type()},...]) -> proper_types:type().
+frequency(FreqChoices) -> weighted_union(FreqChoices).
+
+%% @equiv exactly(E)
+-spec return(term()) -> proper_types:type().
+return(E) -> exactly(E).
+
+%% @doc Adds a default value, `Default', to `Type'.
+%% The default serves as a primary shrinking target for instances, while it
+%% is also chosen by the random instance generation subsystem half the time.
+-spec default(raw_type(), raw_type()) -> proper_types:type().
+default(Default, Type) ->
+ union([Default, Type]).
+
+%% @doc All sorted lists containing elements of type `ElemType'.
+%% Instances shrink towards the empty list, `[]'.
+-spec orderedlist(ElemType::raw_type()) -> proper_types:type().
+orderedlist(RawElemType) ->
+ ?LET(L, list(RawElemType), lists:sort(L)).
+
+%% @equiv function(0, RetType)
+-spec function0(raw_type()) -> proper_types:type().
+function0(RetType) ->
+ function(0, RetType).
+
+%% @equiv function(1, RetType)
+-spec function1(raw_type()) -> proper_types:type().
+function1(RetType) ->
+ function(1, RetType).
+
+%% @equiv function(2, RetType)
+-spec function2(raw_type()) -> proper_types:type().
+function2(RetType) ->
+ function(2, RetType).
+
+%% @equiv function(3, RetType)
+-spec function3(raw_type()) -> proper_types:type().
+function3(RetType) ->
+ function(3, RetType).
+
+%% @equiv function(4, RetType)
+-spec function4(raw_type()) -> proper_types:type().
+function4(RetType) ->
+ function(4, RetType).
+
+%% @doc A specialization of {@link default/2}, where `Default' and `Type' are
+%% assigned weights to be considered by the random instance generator. The
+%% shrinking subsystem will ignore the weights and try to shrink using the
+%% default value.
+-spec weighted_default({frequency(),raw_type()}, {frequency(),raw_type()}) ->
+ proper_types:type().
+weighted_default(Default, Type) ->
+ weighted_union([Default, Type]).
+
+
+%%------------------------------------------------------------------------------
+%% Additional type specification functions
+%%------------------------------------------------------------------------------
+
+%% @doc Overrides the `size' parameter used when generating instances of
+%% `Type' with `NewSize'. Has no effect on size-less types, such as unions.
+%% Also, this will not affect the generation of any internal types contained in
+%% `Type', such as the elements of a list - those will still be generated
+%% using the test-wide value of `size'. One use of this function is to modify
+%% types to produce instances that grow faster or slower, like so:
+%% ```?SIZED(Size, resize(Size * 2, list(integer()))'''
+%% The above specifies a list type that grows twice as fast as normal lists.
+-spec resize(size(), Type::raw_type()) -> proper_types:type().
+resize(NewSize, RawType) ->
+ Type = cook_outer(RawType),
+ case find_prop(size_transform, Type) of
+ {ok,Transform} ->
+ add_prop(size_transform, fun(_S) -> Transform(NewSize) end, Type);
+ error ->
+ add_prop(size_transform, fun(_S) -> NewSize end, Type)
+ end.
+
+%% @doc This is a predefined constraint that can be applied to random-length
+%% list and binary types to ensure that the produced values are never empty.
+%%
+%% e.g. {@link list/0}, {@link string/0}, {@link binary/0})
+-spec non_empty(ListType::raw_type()) -> proper_types:type().
+non_empty(RawListType) ->
+ ?SUCHTHAT(L, RawListType, L =/= [] andalso L =/= <<>>).
+
+%% @doc Creates a new type which is equivalent to `Type', but whose instances
+%% are never shrunk by the shrinking subsystem.
+-spec noshrink(Type::raw_type()) -> proper_types:type().
+noshrink(RawType) ->
+ add_prop(noshrink, true, cook_outer(RawType)).
+
+%% @doc Associates the atom key `Parameter' with the value `Value' while
+%% generating instances of `Type'.
+-spec with_parameter(atom(), value(), Type::raw_type()) -> proper_types:type().
+with_parameter(Parameter, Value, RawType) ->
+ with_parameters([{Parameter,Value}], RawType).
+
+%% @doc Similar to {@link with_parameter/3}, but accepts a list of
+%% `{Parameter, Value}' pairs.
+-spec with_parameters([{atom(),value()}], Type::raw_type()) ->
+ proper_types:type().
+with_parameters(PVlist, RawType) ->
+ Type = cook_outer(RawType),
+ case find_prop(parameters, Type) of
+ {ok,Params} when is_list(Params) ->
+ append_list_to_prop(parameters, PVlist, Type);
+ error ->
+ add_prop(parameters, PVlist, Type)
+ end.
+
+%% @doc Returns the value associated with `Parameter', or `Default' in case
+%% `Parameter' is not associated with any value.
+-spec parameter(atom(), value()) -> value().
+parameter(Parameter, Default) ->
+ Parameters =
+ case erlang:get('$parameters') of
+ undefined -> [];
+ List -> List
+ end,
+ proplists:get_value(Parameter, Parameters, Default).
+
+%% @equiv parameter(Parameter, undefined)
+-spec parameter(atom()) -> value().
+parameter(Parameter) ->
+ parameter(Parameter, undefined).
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl
new file mode 100644
index 0000000000..b16075763f
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl
@@ -0,0 +1,2411 @@
+%%% Copyright 2010-2016 Manolis Papadakis <[email protected]>,
+%%% Eirini Arvaniti <[email protected]>
+%%% and Kostis Sagonas <[email protected]>
+%%%
+%%% This file is part of PropEr.
+%%%
+%%% PropEr is free software: you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation, either version 3 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% PropEr is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with PropEr. If not, see <http://www.gnu.org/licenses/>.
+
+%%% @copyright 2010-2016 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
+%%% @version {@version}
+%%% @author Manolis Papadakis
+
+%%% @doc Erlang type system - PropEr type system integration module.
+%%%
+%%% PropEr can parse types expressed in Erlang's type language and convert them
+%%% to its own type format. Such expressions can be used instead of regular type
+%%% constructors in the second argument of `?FORALL's. No extra notation is
+%%% required; PropEr will detect which calls correspond to native types by
+%%% applying a parse transform during compilation. This parse transform is
+%%% automatically applied to any module that includes the `proper.hrl' header
+%%% file. You can disable this feature by compiling your modules with
+%%% `-DPROPER_NO_TRANS'. Note that this will currently also disable the
+%%% automatic exporting of properties.
+%%%
+%%% The use of native types in properties is subject to the following usage
+%%% rules:
+%%% <ul>
+%%% <li>Native types cannot be used outside of `?FORALL's.</li>
+%%% <li>Inside `?FORALL's, native types can be combined with other native
+%%% types, and even with PropEr types, inside tuples and lists (the constructs
+%%% `[...]', `{...}' and `++' are all allowed).</li>
+%%% <li>All other constructs of Erlang's built-in type system (e.g. `|' for
+%%% union, `_' as an alias of `any()', `<<_:_>>' binary type syntax and
+%%% `fun((...) -> ...)' function type syntax) are not allowed in `?FORALL's,
+%%% because they are rejected by the Erlang parser.</li>
+%%% <li>Anything other than a tuple constructor, list constructor, `++'
+%%% application, local or remote call will automatically be considered a
+%%% PropEr type constructor and not be processed further by the parse
+%%% transform.</li>
+%%% <li>Parametric native types are fully supported; of course, they can only
+%%% appear instantiated in a `?FORALL'. The arguments of parametric native
+%%% types are always interpreted as native types.</li>
+%%% <li>Parametric PropEr types, on the other hand, can take any kind of
+%%% argument. You can even mix native and PropEr types in the arguments of a
+%%% PropEr type. For example, assuming that the following declarations are
+%%% present:
+%%% ``` my_proper_type() -> ?LET(...).
+%%% -type my_native_type() :: ... .'''
+%%% Then the following expressions are all legal:
+%%% ``` vector(2, my_native_type())
+%%% function(0, my_native_type())
+%%% union([my_proper_type(), my_native_type()])''' </li>
+%%% <li>Some type constructors can take native types as arguments (but only
+%%% inside `?FORALL's):
+%%% <ul>
+%%% <li>`?SUCHTHAT', `?SUCHTHATMAYBE', `non_empty', `noshrink': these work
+%%% with native types too</li>
+%%% <li>`?LAZY', `?SHRINK', `resize', `?SIZED': these don't work with native
+%%% types</li>
+%%% <li>`?LET', `?LETSHRINK': only the top-level base type can be a native
+%%% type</li>
+%%% </ul></li>
+%%% <li>Native type declarations in the `?FORALL's of a module can reference any
+%%% custom type declared in a `-type' or `-opaque' attribute of the same
+%%% module, as long as no module identifier is used.</li>
+%%% <li>Typed records cannot be referenced inside `?FORALL's using the
+%%% `#rec_name{}' syntax. To use a typed record in a `?FORALL', enclose the
+%%% record in a custom type like so:
+%%% ``` -type rec_name() :: #rec_name{}. '''
+%%% and use the custom type instead.</li>
+%%% <li>`?FORALL's may contain references to self-recursive or mutually
+%%% recursive native types, so long as each type in the hierarchy has a clear
+%%% base case.
+%%% Currently, PropEr requires that the toplevel of any recursive type
+%%% declaration is either a (maybe empty) list or a union containing at least
+%%% one choice that doesn't reference the type directly (it may, however,
+%%% reference any of the types that are mutually recursive with it). This
+%%% means, for example, that some valid recursive type declarations, such as
+%%% this one:
+%%% ``` ?FORALL(..., a(), ...) '''
+%%% where:
+%%% ``` -type a() :: {'a','none' | a()}. '''
+%%% are not accepted by PropEr. However, such types can be rewritten in a way
+%%% that allows PropEr to parse them:
+%%% ``` ?FORALL(..., a(), ...) '''
+%%% where:
+%%% ``` -type a() :: {'a','none'} | {'a',a()}. '''
+%%% This also means that recursive record declarations are not allowed:
+%%% ``` ?FORALL(..., rec(), ...) '''
+%%% where:
+%%% ``` -type rec() :: #rec{}.
+%%% -record(rec, {a = 0 :: integer(), b = 'nil' :: 'nil' | #rec{}}). '''
+%%% A little rewritting can usually remedy this problem as well:
+%%% ``` ?FORALL(..., rec(), ...) '''
+%%% where:
+%%% ``` -type rec() :: #rec{b :: 'nil'} | #rec{b :: rec()}.
+%%% -record(rec, {a = 0 :: integer(), b = 'nil' :: 'nil' | #rec{}}). '''
+%%% </li>
+%%% <li>Remote types may be referenced in a `?FORALL', so long as they are
+%%% exported from the remote module. Currently, PropEr requires that any
+%%% remote modules whose types are directly referenced from within properties
+%%% are present in the code path at compile time, either compiled with
+%%% `debug_info' enabled or in source form. If PropEr cannot find a remote
+%%% module at all, finds only a compiled object file with no debug
+%%% information or fails to compile the source file, all calls to that module
+%%% will automatically be considered calls to PropEr type constructors.</li>
+%%% <li>For native types to be translated correctly, both the module that
+%%% contains the `?FORALL' declaration as well as any module that contains
+%%% the declaration of a type referenced (directly or indirectly) from inside
+%%% a `?FORALL' must be present in the code path at runtime, either compiled
+%%% with `debug_info' enabled or in source form.</li>
+%%% <li>Local types with the same name as an auto-imported BIF are not accepted
+%%% by PropEr, unless the BIF in question has been declared in a
+%%% `no_auto_import' option.</li>
+%%% <li>When an expression can be interpreted both as a PropEr type and as a
+%%% native type, the former takes precedence. This means that a function
+%%% `foo()' will shadow a type `foo()' if they are both present in the module.
+%%% The same rule applies to remote functions and types as well.</li>
+%%% <li>The above may cause some confusion when list syntax is used:
+%%% <ul>
+%%% <li>The expression `[integer()]' can be interpreted both ways, so the
+%%% PropEr way applies. Therefore, instances of this type will always be
+%%% lists of length 1, not arbitrary integer lists, as would be expected
+%%% when interpreting the expression as a native type.</li>
+%%% <li>Assuming that a custom type foo/1 has been declared, the expression
+%%% `foo([integer()])' can only be interpreted as a native type declaration,
+%%% which means that the generic type of integer lists will be passed to
+%%% `foo/1'.</li>
+%%% </ul></li>
+%%% <li>Currently, PropEr does not detect the following mistakes:
+%%% <ul>
+%%% <li>inline record-field specializations that reference non-existent
+%%% fields</li>
+%%% <li>type parameters that are not present in the RHS of a `-type'
+%%% declaration</li>
+%%% <li>using `_' as a type variable in the LHS of a `-type' declaration</li>
+%%% <li>using the same variable in more than one position in the LHS of a
+%%% `-type' declaration</li>
+%%% </ul>
+%%% </li>
+%%% </ul>
+%%%
+%%% You can use <a href="#index">these</a> functions to try out the type
+%%% translation subsystem.
+%%%
+%%% CAUTION: These functions should never be used inside properties. They are
+%%% meant for demonstration purposes only.
+
+-module(proper_typeserver).
+-behaviour(gen_server).
+-export([demo_translate_type/2, demo_is_instance/3]).
+
+-export([start/0, restart/0, stop/0, create_spec_test/3, get_exp_specced/1,
+ is_instance/3, translate_type/1]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
+-export([get_exp_info/1, match/2]).
+
+-export_type([imm_type/0, mod_exp_types/0, mod_exp_funs/0]).
+
+-include("proper_internal.hrl").
+
+
+%%------------------------------------------------------------------------------
+%% Macros
+%%------------------------------------------------------------------------------
+
+-define(SRC_FILE_EXT, ".erl").
+
+-ifdef(AT_LEAST_19).
+-define(anno(L), erl_anno:new(L)).
+-else.
+-define(anno(L), L).
+-endif.
+
+%% Starting with 18.0 we need to handle both 'type' and 'user_type' tags;
+%% prior Erlang/OTP releases had only 'type' as a tag.
+-define(IS_TYPE_TAG(T), (T =:= type orelse T =:= user_type)).
+
+%% CAUTION: all these must be sorted
+-define(STD_TYPES_0,
+ [any,arity,atom,binary,bitstring,bool,boolean,byte,char,float,integer,
+ list,neg_integer,non_neg_integer,number,pos_integer,string,term,
+ timeout]).
+-define(HARD_ADTS,
+ %% gb_trees:iterator and gb_sets:iterator are NOT hardcoded
+ [{{array,0},array}, {{array,1},proper_array},
+ {{dict,0},dict}, {{dict,2},proper_dict},
+ {{gb_set,0},gb_sets}, {{gb_set,1},proper_gb_sets},
+ {{gb_tree,0},gb_trees}, {{gb_tree,2},proper_gb_trees},
+ {{orddict,2},proper_orddict},
+ {{ordset,1},proper_ordsets},
+ {{queue,0},queue}, {{queue,1},proper_queue},
+ {{set,0},sets}, {{set,1},proper_sets}]).
+-define(HARD_ADT_MODS,
+ [{array, [{{array,0},
+ {{type,0,record,[{atom,0,array}]},[]}}]},
+ {dict, [{{dict,0},
+ {{type,0,record,[{atom,0,dict}]},[]}}]},
+ {gb_sets, [{{gb_set,0},
+ {{type,0,tuple,[{type,0,non_neg_integer,[]},
+ {type,0,gb_set_node,[]}]},[]}}]},
+ {gb_trees, [{{gb_tree,0},
+ {{type,0,tuple,[{type,0,non_neg_integer,[]},
+ {type,0,gb_tree_node,[]}]},[]}}]},
+ %% Our parametric ADTs are already declared as normal types, we just
+ %% need to change them to opaques.
+ {proper_array, [{{array,1},already_declared}]},
+ {proper_dict, [{{dict,2},already_declared}]},
+ {proper_gb_sets, [{{gb_set,1},already_declared},
+ {{iterator,1},already_declared}]},
+ {proper_gb_trees, [{{gb_tree,2},already_declared},
+ {{iterator,2},already_declared}]},
+ {proper_orddict, [{{orddict,2},already_declared}]},
+ {proper_ordsets, [{{ordset,1},already_declared}]},
+ {proper_queue, [{{queue,1},already_declared}]},
+ {proper_sets, [{{set,1},already_declared}]},
+ {queue, [{{queue,0},
+ {{type,0,tuple,[{type,0,list,[]},{type,0,list,[]}]},[]}}]},
+ {sets, [{{set,0},
+ {{type,0,record,[{atom,0,set}]},[]}}]}]).
+
+
+%%------------------------------------------------------------------------------
+%% Types
+%%------------------------------------------------------------------------------
+
+-type type_name() :: atom().
+-type var_name() :: atom(). %% TODO: also integers?
+-type field_name() :: atom().
+
+-type type_kind() :: 'type' | 'record'.
+-type type_ref() :: {type_kind(),type_name(),arity()}.
+-ifdef(NO_MODULES_IN_OPAQUES).
+-type substs_dict() :: dict(). %% dict(field_name(),ret_type())
+-else.
+-type substs_dict() :: dict:dict(field_name(),ret_type()).
+-endif.
+-type full_type_ref() :: {mod_name(),type_kind(),type_name(),
+ [ret_type()] | substs_dict()}.
+-type symb_info() :: 'not_symb' | {'orig_abs',abs_type()}.
+-type type_repr() :: {'abs_type',abs_type(),[var_name()],symb_info()}
+ | {'cached',fin_type(),abs_type(),symb_info()}
+ | {'abs_record',[{field_name(),abs_type()}]}.
+-type gen_fun() :: fun((size()) -> fin_type()).
+-type rec_fun() :: fun(([gen_fun()],size()) -> fin_type()).
+-type rec_arg() :: {boolean() | {'list',boolean(),rec_fun()},full_type_ref()}.
+-type rec_args() :: [rec_arg()].
+-type ret_type() :: {'simple',fin_type()} | {'rec',rec_fun(),rec_args()}.
+-type rec_fun_info() :: {pos_integer(),pos_integer(),[arity(),...],
+ [rec_fun(),...]}.
+
+-type imm_type_ref() :: {type_name(),arity()}.
+-type hard_adt_repr() :: {abs_type(),[var_name()]} | 'already_declared'.
+-type fun_ref() :: {fun_name(),arity()}.
+-type fun_repr() :: fun_clause_repr().
+-type fun_clause_repr() :: {[abs_type()],abs_type()}.
+-type proc_fun_ref() :: {fun_name(),[abs_type()],abs_type()}.
+-type full_imm_type_ref() :: {mod_name(),type_name(),arity()}.
+-type imm_stack() :: [full_imm_type_ref()].
+-type pat_field() :: 0 | 1 | atom().
+-type pattern() :: loose_tuple(pat_field()).
+-type next_step() :: 'none' | 'take_head' | {'match_with',pattern()}.
+
+-ifdef(NO_MODULES_IN_OPAQUES).
+%% @private_type
+-type mod_exp_types() :: set(). %% set(imm_type_ref())
+-type mod_types() :: dict(). %% dict(type_ref(),type_repr())
+%% @private_type
+-type mod_exp_funs() :: set(). %% set(fun_ref())
+-type mod_specs() :: dict(). %% dict(fun_ref(),fun_repr())
+-else.
+%% @private_type
+-type mod_exp_types() :: sets:set(imm_type_ref()).
+-type mod_types() :: dict:dict(type_ref(),type_repr()).
+%% @private_type
+-type mod_exp_funs() :: sets:set(fun_ref()).
+-type mod_specs() :: dict:dict(fun_ref(),fun_repr()).
+-endif.
+
+-ifdef(NO_MODULES_IN_OPAQUES).
+-record(state,
+ {cached = dict:new() :: dict(), %% dict(imm_type(),fin_type())
+ exp_types = dict:new() :: dict(), %% dict(mod_name(),mod_exp_types())
+ types = dict:new() :: dict(), %% dict(mod_name(),mod_types())
+ exp_specs = dict:new() :: dict()}). %% dict(mod_name(),mod_specs())
+-else.
+-record(state,
+ {cached = dict:new() :: dict:dict(imm_type(),fin_type()),
+ exp_types = dict:new() :: dict:dict(mod_name(),mod_exp_types()),
+ types = dict:new() :: dict:dict(mod_name(),mod_types()),
+ exp_specs = dict:new() :: dict:dict(mod_name(),mod_specs())}).
+-endif.
+-type state() :: #state{}.
+
+-record(mod_info,
+ {mod_exp_types = sets:new() :: mod_exp_types(),
+ mod_types = dict:new() :: mod_types(),
+ mod_opaques = sets:new() :: mod_exp_types(),
+ mod_exp_funs = sets:new() :: mod_exp_funs(),
+ mod_specs = dict:new() :: mod_specs()}).
+-type mod_info() :: #mod_info{}.
+
+-type stack() :: [full_type_ref() | 'tuple' | 'list' | 'union' | 'fun'].
+-ifdef(NO_MODULES_IN_OPAQUES).
+-type var_dict() :: dict(). %% dict(var_name(),ret_type())
+-else.
+-type var_dict() :: dict:dict(var_name(),ret_type()).
+-endif.
+%% @private_type
+-type imm_type() :: {mod_name(),string()}.
+%% @alias
+-type fin_type() :: proper_types:type().
+-type tagged_result(T) :: {'ok',T} | 'error'.
+-type tagged_result2(T,S) :: {'ok',T,S} | 'error'.
+%% @alias
+-type rich_result(T) :: {'ok',T} | {'error',term()}.
+-type rich_result2(T,S) :: {'ok',T,S} | {'error',term()}.
+-type false_positive_mfas() :: proper:false_positive_mfas().
+
+-type server_call() :: {'create_spec_test',mfa(),timeout(),false_positive_mfas()}
+ | {'get_exp_specced',mod_name()}
+ | {'get_type_repr',mod_name(),type_ref(),boolean()}
+ | {'translate_type',imm_type()}.
+-type server_response() :: rich_result(proper:test())
+ | rich_result([mfa()])
+ | rich_result(type_repr())
+ | rich_result(fin_type()).
+
+
+%%------------------------------------------------------------------------------
+%% Server interface functions
+%%------------------------------------------------------------------------------
+
+%% @private
+-spec start() -> 'ok'.
+start() ->
+ {ok,TypeserverPid} = gen_server:start_link(?MODULE, dummy, []),
+ put('$typeserver_pid', TypeserverPid),
+ ok.
+
+%% @private
+-spec restart() -> 'ok'.
+restart() ->
+ TypeserverPid = get('$typeserver_pid'),
+ case (TypeserverPid =:= undefined orelse not is_process_alive(TypeserverPid)) of
+ true -> start();
+ false -> ok
+ end.
+
+%% @private
+-spec stop() -> 'ok'.
+stop() ->
+ TypeserverPid = get('$typeserver_pid'),
+ erase('$typeserver_pid'),
+ gen_server:cast(TypeserverPid, stop).
+
+%% @private
+-spec create_spec_test(mfa(), timeout(), false_positive_mfas()) -> rich_result(proper:test()).
+create_spec_test(MFA, SpecTimeout, FalsePositiveMFAs) ->
+ TypeserverPid = get('$typeserver_pid'),
+ gen_server:call(TypeserverPid, {create_spec_test,MFA,SpecTimeout,FalsePositiveMFAs}).
+
+%% @private
+-spec get_exp_specced(mod_name()) -> rich_result([mfa()]).
+get_exp_specced(Mod) ->
+ TypeserverPid = get('$typeserver_pid'),
+ gen_server:call(TypeserverPid, {get_exp_specced,Mod}).
+
+-spec get_type_repr(mod_name(), type_ref(), boolean()) ->
+ rich_result(type_repr()).
+get_type_repr(Mod, TypeRef, IsRemote) ->
+ TypeserverPid = get('$typeserver_pid'),
+ gen_server:call(TypeserverPid, {get_type_repr,Mod,TypeRef,IsRemote}).
+
+%% @private
+-spec translate_type(imm_type()) -> rich_result(fin_type()).
+translate_type(ImmType) ->
+ TypeserverPid = get('$typeserver_pid'),
+ gen_server:call(TypeserverPid, {translate_type,ImmType}).
+
+%% @doc Translates the native type expression `TypeExpr' (which should be
+%% provided inside a string) into a PropEr type, which can then be passed to any
+%% of the demo functions defined in the {@link proper_gen} module. PropEr acts
+%% as if it found this type expression inside the code of module `Mod'.
+-spec demo_translate_type(mod_name(), string()) -> rich_result(fin_type()).
+demo_translate_type(Mod, TypeExpr) ->
+ start(),
+ Result = translate_type({Mod,TypeExpr}),
+ stop(),
+ Result.
+
+%% @doc Checks if `Term' is a valid instance of native type `TypeExpr' (which
+%% should be provided inside a string). PropEr acts as if it found this type
+%% expression inside the code of module `Mod'.
+-spec demo_is_instance(term(), mod_name(), string()) ->
+ boolean() | {'error',term()}.
+demo_is_instance(Term, Mod, TypeExpr) ->
+ case parse_type(TypeExpr) of
+ {ok,TypeForm} ->
+ start(),
+ Result =
+ %% Force the typeserver to load the module.
+ case translate_type({Mod,"integer()"}) of
+ {ok,_FinType} ->
+ try is_instance(Term, Mod, TypeForm)
+ catch
+ throw:{'$typeserver',Reason} -> {error, Reason}
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end,
+ stop(),
+ Result;
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+
+%%------------------------------------------------------------------------------
+%% Implementation of gen_server interface
+%%------------------------------------------------------------------------------
+
+%% @private
+-spec init(_) -> {'ok',state()}.
+init(_) ->
+ {ok, #state{}}.
+
+%% @private
+-spec handle_call(server_call(), _, state()) ->
+ {'reply',server_response(),state()}.
+handle_call({create_spec_test,MFA,SpecTimeout,FalsePositiveMFAs}, _From, State) ->
+ case create_spec_test(MFA, SpecTimeout, FalsePositiveMFAs, State) of
+ {ok,Test,NewState} ->
+ {reply, {ok,Test}, NewState};
+ {error,_Reason} = Error ->
+ {reply, Error, State}
+ end;
+handle_call({get_exp_specced,Mod}, _From, State) ->
+ case get_exp_specced(Mod, State) of
+ {ok,MFAs,NewState} ->
+ {reply, {ok,MFAs}, NewState};
+ {error,_Reason} = Error ->
+ {reply, Error, State}
+ end;
+handle_call({get_type_repr,Mod,TypeRef,IsRemote}, _From, State) ->
+ case get_type_repr(Mod, TypeRef, IsRemote, State) of
+ {ok,TypeRepr,NewState} ->
+ {reply, {ok,TypeRepr}, NewState};
+ {error,_Reason} = Error ->
+ {reply, Error, State}
+ end;
+handle_call({translate_type,ImmType}, _From, State) ->
+ case translate_type(ImmType, State) of
+ {ok,FinType,NewState} ->
+ {reply, {ok,FinType}, NewState};
+ {error,_Reason} = Error ->
+ {reply, Error, State}
+ end.
+
+%% @private
+-spec handle_cast('stop', state()) -> {'stop','normal',state()}.
+handle_cast(stop, State) ->
+ {stop, normal, State}.
+
+%% @private
+-spec handle_info(term(), state()) -> {'stop',{'received_info',term()},state()}.
+handle_info(Info, State) ->
+ {stop, {received_info,Info}, State}.
+
+%% @private
+-spec terminate(term(), state()) -> 'ok'.
+terminate(_Reason, _State) ->
+ ok.
+
+%% @private
+-spec code_change(term(), state(), _) -> {'ok',state()}.
+code_change(_OldVsn, State, _) ->
+ {ok, State}.
+
+
+%%------------------------------------------------------------------------------
+%% Top-level interface
+%%------------------------------------------------------------------------------
+
+-spec create_spec_test(mfa(), timeout(), false_positive_mfas(), state()) ->
+ rich_result2(proper:test(),state()).
+create_spec_test(MFA, SpecTimeout, FalsePositiveMFAs, State) ->
+ case get_exp_spec(MFA, State) of
+ {ok,FunRepr,NewState} ->
+ make_spec_test(MFA, FunRepr, SpecTimeout, FalsePositiveMFAs, NewState);
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec get_exp_spec(mfa(), state()) -> rich_result2(fun_repr(),state()).
+get_exp_spec({Mod,Fun,Arity} = MFA, State) ->
+ case add_module(Mod, State) of
+ {ok,#state{exp_specs = ExpSpecs} = NewState} ->
+ ModExpSpecs = dict:fetch(Mod, ExpSpecs),
+ case dict:find({Fun,Arity}, ModExpSpecs) of
+ {ok,FunRepr} ->
+ {ok, FunRepr, NewState};
+ error ->
+ {error, {function_not_exported_or_specced,MFA}}
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec make_spec_test(mfa(), fun_repr(), timeout(), false_positive_mfas(), state()) ->
+ rich_result2(proper:test(),state()).
+make_spec_test({Mod,_Fun,_Arity}=MFA, {Domain,_Range}=FunRepr, SpecTimeout, FalsePositiveMFAs, State) ->
+ case convert(Mod, {type,?anno(0),'$fixed_list',Domain}, State) of
+ {ok,FinType,NewState} ->
+ Test = ?FORALL(Args, FinType, apply_spec_test(MFA, FunRepr, SpecTimeout, FalsePositiveMFAs, Args)),
+ {ok, Test, NewState};
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec apply_spec_test(mfa(), fun_repr(), timeout(), false_positive_mfas(), term()) -> proper:test().
+apply_spec_test({Mod,Fun,_Arity}=MFA, {_Domain,Range}, SpecTimeout, FalsePositiveMFAs, Args) ->
+ ?TIMEOUT(SpecTimeout,
+ begin
+ %% NOTE: only call apply/3 inside try/catch (do not trust ?MODULE:is_instance/3)
+ Result =
+ try apply(Mod, Fun, Args) of
+ X -> {ok, X}
+ catch
+ X:Y -> {X, Y}
+ end,
+ case Result of
+ {ok, Z} ->
+ case ?MODULE:is_instance(Z, Mod, Range) of
+ true ->
+ true;
+ false when is_function(FalsePositiveMFAs) ->
+ FalsePositiveMFAs(MFA, Args, {fail, Z});
+ false ->
+ false
+ end;
+ Exception when is_function(FalsePositiveMFAs) ->
+ case FalsePositiveMFAs(MFA, Args, Exception) of
+ true ->
+ true;
+ false ->
+ error(Exception, erlang:get_stacktrace())
+ end;
+ Exception ->
+ error(Exception, erlang:get_stacktrace())
+ end
+ end).
+
+-spec get_exp_specced(mod_name(), state()) -> rich_result2([mfa()],state()).
+get_exp_specced(Mod, State) ->
+ case add_module(Mod, State) of
+ {ok,#state{exp_specs = ExpSpecs} = NewState} ->
+ ModExpSpecs = dict:fetch(Mod, ExpSpecs),
+ ExpSpecced = [{Mod,F,A} || {F,A} <- dict:fetch_keys(ModExpSpecs)],
+ {ok, ExpSpecced, NewState};
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec get_type_repr(mod_name(), type_ref(), boolean(), state()) ->
+ rich_result2(type_repr(),state()).
+get_type_repr(Mod, {type,Name,Arity} = TypeRef, true, State) ->
+ case prepare_for_remote(Mod, Name, Arity, State) of
+ {ok,NewState} ->
+ get_type_repr(Mod, TypeRef, false, NewState);
+ {error,_Reason} = Error ->
+ Error
+ end;
+get_type_repr(Mod, TypeRef, false, #state{types = Types} = State) ->
+ ModTypes = dict:fetch(Mod, Types),
+ case dict:find(TypeRef, ModTypes) of
+ {ok,TypeRepr} ->
+ {ok, TypeRepr, State};
+ error ->
+ {error, {missing_type,Mod,TypeRef}}
+ end.
+
+-spec prepare_for_remote(mod_name(), type_name(), arity(), state()) ->
+ rich_result(state()).
+prepare_for_remote(RemMod, Name, Arity, State) ->
+ case add_module(RemMod, State) of
+ {ok,#state{exp_types = ExpTypes} = NewState} ->
+ RemModExpTypes = dict:fetch(RemMod, ExpTypes),
+ case sets:is_element({Name,Arity}, RemModExpTypes) of
+ true -> {ok, NewState};
+ false -> {error, {type_not_exported,{RemMod,Name,Arity}}}
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec translate_type(imm_type(), state()) -> rich_result2(fin_type(),state()).
+translate_type({Mod,Str} = ImmType, #state{cached = Cached} = State) ->
+ case dict:find(ImmType, Cached) of
+ {ok,Type} ->
+ {ok, Type, State};
+ error ->
+ case parse_type(Str) of
+ {ok,TypeForm} ->
+ case add_module(Mod, State) of
+ {ok,NewState} ->
+ case convert(Mod, TypeForm, NewState) of
+ {ok,FinType,
+ #state{cached = Cached} = FinalState} ->
+ NewCached = dict:store(ImmType, FinType,
+ Cached),
+ {ok, FinType,
+ FinalState#state{cached = NewCached}};
+ {error,_Reason} = Error ->
+ Error
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end;
+ {error,Reason} ->
+ {error, {parse_error,Str,Reason}}
+ end
+ end.
+
+-spec parse_type(string()) -> rich_result(abs_type()).
+parse_type(Str) ->
+ TypeStr = "-type mytype() :: " ++ Str ++ ".",
+ case erl_scan:string(TypeStr) of
+ {ok,Tokens,_EndLocation} ->
+ case erl_parse:parse_form(Tokens) of
+ {ok,{attribute,_Line,type,{mytype,TypeExpr,[]}}} ->
+ {ok, TypeExpr};
+ {error,_ErrorInfo} = Error ->
+ Error
+ end;
+ {error,ErrorInfo,_EndLocation} ->
+ {error, ErrorInfo}
+ end.
+
+-spec add_module(mod_name(), state()) -> rich_result(state()).
+add_module(Mod, #state{exp_types = ExpTypes} = State) ->
+ case dict:is_key(Mod, ExpTypes) of
+ true ->
+ {ok, State};
+ false ->
+ case get_code_and_exports(Mod) of
+ {ok,AbsCode,ModExpFuns} ->
+ RawModInfo = get_mod_info(Mod, AbsCode, ModExpFuns),
+ ModInfo = process_adts(Mod, RawModInfo),
+ {ok, store_mod_info(Mod, ModInfo, State)};
+ {error,Reason} ->
+ {error, {cant_load_code,Mod,Reason}}
+ end
+ end.
+
+%% @private
+-spec get_exp_info(mod_name()) -> rich_result2(mod_exp_types(),mod_exp_funs()).
+get_exp_info(Mod) ->
+ case get_code_and_exports(Mod) of
+ {ok,AbsCode,ModExpFuns} ->
+ RawModInfo = get_mod_info(Mod, AbsCode, ModExpFuns),
+ {ok, RawModInfo#mod_info.mod_exp_types, ModExpFuns};
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec get_code_and_exports(mod_name()) ->
+ rich_result2([abs_form()],mod_exp_funs()).
+get_code_and_exports(Mod) ->
+ case code:get_object_code(Mod) of
+ {Mod, ObjBin, _ObjFileName} ->
+ case get_chunks(ObjBin) of
+ {ok,_AbsCode,_ModExpFuns} = Result ->
+ Result;
+ {error,Reason} ->
+ get_code_and_exports_from_source(Mod, Reason)
+ end;
+ error ->
+ get_code_and_exports_from_source(Mod, cant_find_object_file)
+ end.
+
+-spec get_code_and_exports_from_source(mod_name(), term()) ->
+ rich_result2([abs_form()],mod_exp_funs()).
+get_code_and_exports_from_source(Mod, ObjError) ->
+ SrcFileName = atom_to_list(Mod) ++ ?SRC_FILE_EXT,
+ case code:where_is_file(SrcFileName) of
+ FullSrcFileName when is_list(FullSrcFileName) ->
+ Opts = [binary,debug_info,return_errors,{d,'PROPER_REMOVE_PROPS'}],
+ case compile:file(FullSrcFileName, Opts) of
+ {ok,Mod,Binary} ->
+ get_chunks(Binary);
+ {error,Errors,_Warnings} ->
+ {error, {ObjError,{cant_compile_source_file,Errors}}}
+ end;
+ non_existing ->
+ {error, {ObjError,cant_find_source_file}}
+ end.
+
+-spec get_chunks(string() | binary()) ->
+ rich_result2([abs_form()],mod_exp_funs()).
+get_chunks(ObjFile) ->
+ case beam_lib:chunks(ObjFile, [abstract_code,exports]) of
+ {ok,{_Mod,[{abstract_code,AbsCodeChunk},{exports,ExpFunsList}]}} ->
+ case AbsCodeChunk of
+ {raw_abstract_v1,AbsCode} ->
+ %% HACK: Add a declaration for iolist() to every module
+ {ok, add_iolist(AbsCode), sets:from_list(ExpFunsList)};
+ no_abstract_code ->
+ {error, no_abstract_code};
+ _ ->
+ {error, unsupported_abstract_code_format}
+ end;
+ {error,beam_lib,Reason} ->
+ {error, Reason}
+ end.
+
+-spec add_iolist([abs_form()]) -> [abs_form()].
+add_iolist(Forms) ->
+ IOListDef =
+ {type,0,maybe_improper_list,
+ [{type,0,union,[{type,0,byte,[]},{type,0,binary,[]},
+ {type,0,iolist,[]}]},
+ {type,0,binary,[]}]},
+ IOListDecl = {attribute,0,type,{iolist,IOListDef,[]}},
+ [IOListDecl | Forms].
+
+-spec get_mod_info(mod_name(), [abs_form()], mod_exp_funs()) -> mod_info().
+get_mod_info(Mod, AbsCode, ModExpFuns) ->
+ StartModInfo = #mod_info{mod_exp_funs = ModExpFuns},
+ ImmModInfo = lists:foldl(fun add_mod_info/2, StartModInfo, AbsCode),
+ #mod_info{mod_specs = AllModSpecs} = ImmModInfo,
+ IsExported = fun(FunRef,_FunRepr) -> sets:is_element(FunRef,ModExpFuns) end,
+ ModExpSpecs = dict:filter(IsExported, AllModSpecs),
+ ModInfo = ImmModInfo#mod_info{mod_specs = ModExpSpecs},
+ case orddict:find(Mod, ?HARD_ADT_MODS) of
+ {ok,ModADTs} ->
+ #mod_info{mod_exp_types = ModExpTypes, mod_types = ModTypes,
+ mod_opaques = ModOpaques} = ModInfo,
+ ModADTsSet =
+ sets:from_list([ImmTypeRef
+ || {ImmTypeRef,_HardADTRepr} <- ModADTs]),
+ NewModExpTypes = sets:union(ModExpTypes, ModADTsSet),
+ NewModTypes = lists:foldl(fun store_hard_adt/2, ModTypes, ModADTs),
+ NewModOpaques = sets:union(ModOpaques, ModADTsSet),
+ ModInfo#mod_info{mod_exp_types = NewModExpTypes,
+ mod_types = NewModTypes,
+ mod_opaques = NewModOpaques};
+ error ->
+ ModInfo
+ end.
+
+-spec store_hard_adt({imm_type_ref(),hard_adt_repr()}, mod_types()) ->
+ mod_types().
+store_hard_adt({_ImmTypeRef,already_declared}, ModTypes) ->
+ ModTypes;
+store_hard_adt({{Name,Arity},{TypeForm,VarNames}}, ModTypes) ->
+ TypeRef = {type,Name,Arity},
+ TypeRepr = {abs_type,TypeForm,VarNames,not_symb},
+ dict:store(TypeRef, TypeRepr, ModTypes).
+
+-spec add_mod_info(abs_form(), mod_info()) -> mod_info().
+add_mod_info({attribute,_Line,export_type,TypesList},
+ #mod_info{mod_exp_types = ModExpTypes} = ModInfo) ->
+ NewModExpTypes = sets:union(sets:from_list(TypesList), ModExpTypes),
+ ModInfo#mod_info{mod_exp_types = NewModExpTypes};
+add_mod_info({attribute,_Line,type,{{record,RecName},Fields,[]}},
+ #mod_info{mod_types = ModTypes} = ModInfo) ->
+ FieldInfo = [process_rec_field(F) || F <- Fields],
+ NewModTypes = dict:store({record,RecName,0}, {abs_record,FieldInfo},
+ ModTypes),
+ ModInfo#mod_info{mod_types = NewModTypes};
+add_mod_info({attribute,Line,record,{RecName,Fields}},
+ #mod_info{mod_types = ModTypes} = ModInfo) ->
+ case dict:is_key(RecName, ModTypes) of
+ true ->
+ ModInfo;
+ false -> % fake an opaque term by using the same Line as annotation
+ TypedRecord = {attribute,Line,type,{{record,RecName},Fields,[]}},
+ add_mod_info(TypedRecord, ModInfo)
+ end;
+add_mod_info({attribute,_Line,Kind,{Name,TypeForm,VarForms}},
+ #mod_info{mod_types = ModTypes,
+ mod_opaques = ModOpaques} = ModInfo)
+ when Kind =:= type; Kind =:= opaque ->
+ Arity = length(VarForms),
+ VarNames = [V || {var,_,V} <- VarForms],
+ %% TODO: No check whether variables are different, or non-'_'.
+ NewModTypes = dict:store({type,Name,Arity},
+ {abs_type,TypeForm,VarNames,not_symb}, ModTypes),
+ NewModOpaques =
+ case Kind of
+ type -> ModOpaques;
+ opaque -> sets:add_element({Name,Arity}, ModOpaques)
+ end,
+ ModInfo#mod_info{mod_types = NewModTypes, mod_opaques = NewModOpaques};
+add_mod_info({attribute,_Line,spec,{RawFunRef,[RawFirstClause | _Rest]}},
+ #mod_info{mod_specs = ModSpecs} = ModInfo) ->
+ FunRef = case RawFunRef of
+ {_Mod,Name,Arity} -> {Name,Arity};
+ {_Name,_Arity} = F -> F
+ end,
+ %% TODO: We just take the first function clause.
+ FirstClause = process_fun_clause(RawFirstClause),
+ NewModSpecs = dict:store(FunRef, FirstClause, ModSpecs),
+ ModInfo#mod_info{mod_specs = NewModSpecs};
+add_mod_info(_Form, ModInfo) ->
+ ModInfo.
+
+-spec process_rec_field(abs_rec_field()) -> {field_name(),abs_type()}.
+process_rec_field({record_field,_,{atom,_,FieldName}}) ->
+ {FieldName, {type,0,any,[]}};
+process_rec_field({record_field,_,{atom,_,FieldName},_Initialization}) ->
+ {FieldName, {type,0,any,[]}};
+process_rec_field({typed_record_field,RecField,FieldType}) ->
+ {FieldName,_} = process_rec_field(RecField),
+ {FieldName, FieldType}.
+
+-spec process_fun_clause(abs_type()) -> fun_clause_repr().
+process_fun_clause({type,_,'fun',[{type,_,product,Domain},Range]}) ->
+ {Domain, Range};
+process_fun_clause({type,_,bounded_fun,[MainClause,Constraints]}) ->
+ {RawDomain,RawRange} = process_fun_clause(MainClause),
+ VarSubsts = [{V,T} || {type,_,constraint,
+ [{atom,_,is_subtype},[{var,_,V},T]]} <- Constraints,
+ V =/= '_'],
+ VarSubstsDict = dict:from_list(VarSubsts),
+ Domain = [update_vars(A, VarSubstsDict, false) || A <- RawDomain],
+ Range = update_vars(RawRange, VarSubstsDict, false),
+ {Domain, Range}.
+
+-spec store_mod_info(mod_name(), mod_info(), state()) -> state().
+store_mod_info(Mod, #mod_info{mod_exp_types = ModExpTypes, mod_types = ModTypes,
+ mod_specs = ImmModExpSpecs},
+ #state{exp_types = ExpTypes, types = Types,
+ exp_specs = ExpSpecs} = State) ->
+ NewExpTypes = dict:store(Mod, ModExpTypes, ExpTypes),
+ NewTypes = dict:store(Mod, ModTypes, Types),
+ ModExpSpecs = dict:map(fun unbound_to_any/2, ImmModExpSpecs),
+ NewExpSpecs = dict:store(Mod, ModExpSpecs, ExpSpecs),
+ State#state{exp_types = NewExpTypes, types = NewTypes,
+ exp_specs = NewExpSpecs}.
+
+-spec unbound_to_any(fun_ref(), fun_repr()) -> fun_repr().
+unbound_to_any(_FunRef, {Domain,Range}) ->
+ EmptySubstsDict = dict:new(),
+ NewDomain = [update_vars(A,EmptySubstsDict,true) || A <- Domain],
+ NewRange = update_vars(Range, EmptySubstsDict, true),
+ {NewDomain, NewRange}.
+
+
+%%------------------------------------------------------------------------------
+%% ADT translation functions
+%%------------------------------------------------------------------------------
+
+-spec process_adts(mod_name(), mod_info()) -> mod_info().
+process_adts(Mod,
+ #mod_info{mod_exp_types = ModExpTypes, mod_opaques = ModOpaques,
+ mod_specs = ModExpSpecs} = ModInfo) ->
+ %% TODO: No warning on unexported opaques.
+ case sets:to_list(sets:intersection(ModExpTypes,ModOpaques)) of
+ [] ->
+ ModInfo;
+ ModADTs ->
+ %% TODO: No warning on unexported API functions.
+ ModExpSpecsList = [{Name,Domain,Range}
+ || {{Name,_Arity},{Domain,Range}}
+ <- dict:to_list(ModExpSpecs)],
+ AddADT = fun(ADT,Acc) -> add_adt(Mod,ADT,Acc,ModExpSpecsList) end,
+ lists:foldl(AddADT, ModInfo, ModADTs)
+ end.
+
+-spec add_adt(mod_name(), imm_type_ref(), mod_info(), [proc_fun_ref()]) ->
+ mod_info().
+add_adt(Mod, {Name,Arity}, #mod_info{mod_types = ModTypes} = ModInfo,
+ ModExpFunSpecs) ->
+ ADTRef = {type,Name,Arity},
+ {abs_type,InternalRepr,VarNames,not_symb} = dict:fetch(ADTRef, ModTypes),
+ FullADTRef = {Mod,Name,Arity},
+ %% TODO: No warning on unsuitable range.
+ SymbCalls1 = [get_symb_call(FullADTRef,Spec) || Spec <- ModExpFunSpecs],
+ %% TODO: No warning on bad use of variables.
+ SymbCalls2 = [fix_vars(FullADTRef,Call,RangeVars,VarNames)
+ || {ok,Call,RangeVars} <- SymbCalls1],
+ case [Call || {ok,Call} <- SymbCalls2] of
+ [] ->
+ %% TODO: No warning on no acceptable spec.
+ ModInfo;
+ SymbCalls3 ->
+ NewADTRepr = {abs_type,{type,0,union,SymbCalls3},VarNames,
+ {orig_abs,InternalRepr}},
+ NewModTypes = dict:store(ADTRef, NewADTRepr, ModTypes),
+ ModInfo#mod_info{mod_types = NewModTypes}
+ end.
+
+-spec get_symb_call(full_imm_type_ref(), proc_fun_ref()) ->
+ tagged_result2(abs_type(),[var_name()]).
+get_symb_call({Mod,_TypeName,_Arity} = FullADTRef, {FunName,Domain,Range}) ->
+ A = ?anno(0),
+ BaseCall = {type,A,tuple,[{atom,A,'$call'},{atom,A,Mod},{atom,A,FunName},
+ {type,A,'$fixed_list',Domain}]},
+ unwrap_range(FullADTRef, BaseCall, Range, false).
+
+-spec unwrap_range(full_imm_type_ref(), abs_type() | next_step(), abs_type(),
+ boolean()) ->
+ tagged_result2(abs_type() | next_step(),[var_name()]).
+unwrap_range(FullADTRef, Call, {paren_type,_,[Type]}, TestRun) ->
+ unwrap_range(FullADTRef, Call, Type, TestRun);
+unwrap_range(FullADTRef, Call, {ann_type,_,[_Var,Type]}, TestRun) ->
+ unwrap_range(FullADTRef, Call, Type, TestRun);
+unwrap_range(FullADTRef, Call, {type,_,list,[ElemType]}, TestRun) ->
+ unwrap_list(FullADTRef, Call, ElemType, TestRun);
+unwrap_range(FullADTRef, Call, {type,_,maybe_improper_list,[Cont,_Term]},
+ TestRun) ->
+ unwrap_list(FullADTRef, Call, Cont, TestRun);
+unwrap_range(FullADTRef, Call, {type,_,nonempty_list,[ElemType]}, TestRun) ->
+ unwrap_list(FullADTRef, Call, ElemType, TestRun);
+unwrap_range(FullADTRef, Call, {type,_,nonempty_improper_list,[Cont,_Term]},
+ TestRun) ->
+ unwrap_list(FullADTRef, Call, Cont, TestRun);
+unwrap_range(FullADTRef, Call,
+ {type,_,nonempty_maybe_improper_list,[Cont,_Term]}, TestRun) ->
+ unwrap_list(FullADTRef, Call, Cont, TestRun);
+unwrap_range(_FullADTRef, _Call, {type,_,tuple,any}, _TestRun) ->
+ error;
+unwrap_range(FullADTRef, Call, {type,_,tuple,FieldForms}, TestRun) ->
+ Translates = fun(T) -> unwrap_range(FullADTRef,none,T,true) =/= error end,
+ case proper_arith:find_first(Translates, FieldForms) of
+ none ->
+ error;
+ {TargetPos,TargetElem} ->
+ Pattern = get_pattern(TargetPos, FieldForms),
+ case TestRun of
+ true ->
+ NewCall =
+ case Call of
+ none -> {match_with,Pattern};
+ _ -> Call
+ end,
+ {ok, NewCall, []};
+ false ->
+ AbsPattern = term_to_singleton_type(Pattern),
+ A = ?anno(0),
+ NewCall =
+ {type,A,tuple,
+ [{atom,A,'$call'},{atom,A,?MODULE},{atom,A,match},
+ {type,A,'$fixed_list',[AbsPattern,Call]}]},
+ unwrap_range(FullADTRef, NewCall, TargetElem, TestRun)
+ end
+ end;
+unwrap_range(FullADTRef, Call, {type,_,union,Choices}, TestRun) ->
+ TestedChoices = [unwrap_range(FullADTRef,none,C,true) || C <- Choices],
+ NotError = fun(error) -> false; (_) -> true end,
+ case proper_arith:find_first(NotError, TestedChoices) of
+ none ->
+ error;
+ {_ChoicePos,{ok,none,_RangeVars}} ->
+ error;
+ {ChoicePos,{ok,NextStep,_RangeVars}} ->
+ {A, [ChoiceElem|B]} = lists:split(ChoicePos-1, Choices),
+ OtherChoices = A ++ B,
+ DistinctChoice =
+ case NextStep of
+ take_head ->
+ fun cant_have_head/1;
+ {match_with,Pattern} ->
+ fun(C) -> cant_match(Pattern, C) end
+ end,
+ case {lists:all(DistinctChoice,OtherChoices), TestRun} of
+ {true,true} ->
+ {ok, NextStep, []};
+ {true,false} ->
+ unwrap_range(FullADTRef, Call, ChoiceElem, TestRun);
+ {false,_} ->
+ error
+ end
+ end;
+unwrap_range({_Mod,SameName,Arity}, Call, {type,_,SameName,ArgForms},
+ _TestRun) ->
+ RangeVars = [V || {var,_,V} <- ArgForms, V =/= '_'],
+ case length(ArgForms) =:= Arity andalso length(RangeVars) =:= Arity of
+ true -> {ok, Call, RangeVars};
+ false -> error
+ end;
+unwrap_range({SameMod,SameName,_Arity} = FullADTRef, Call,
+ {remote_type,_,[{atom,_,SameMod},{atom,_,SameName},ArgForms]},
+ TestRun) ->
+ unwrap_range(FullADTRef, Call, {type,?anno(0),SameName,ArgForms}, TestRun);
+unwrap_range(_FullADTRef, _Call, _Range, _TestRun) ->
+ error.
+
+-spec unwrap_list(full_imm_type_ref(), abs_type() | next_step(), abs_type(),
+ boolean()) ->
+ tagged_result2(abs_type() | next_step(),[var_name()]).
+unwrap_list(FullADTRef, Call, HeadType, TestRun) ->
+ NewCall =
+ case TestRun of
+ true ->
+ case Call of
+ none -> take_head;
+ _ -> Call
+ end;
+ false ->
+ {type,0,tuple,[{atom,0,'$call'},{atom,0,erlang},{atom,0,hd},
+ {type,0,'$fixed_list',[Call]}]}
+ end,
+ unwrap_range(FullADTRef, NewCall, HeadType, TestRun).
+
+-spec fix_vars(full_imm_type_ref(), abs_type(), [var_name()], [var_name()]) ->
+ tagged_result(abs_type()).
+fix_vars(FullADTRef, Call, RangeVars, VarNames) ->
+ NotAnyVar = fun(V) -> V =/= '_' end,
+ case no_duplicates(VarNames) andalso lists:all(NotAnyVar,VarNames) of
+ true ->
+ RawUsedVars =
+ collect_vars(FullADTRef, Call, [[V] || V <- RangeVars]),
+ UsedVars = [lists:usort(L) || L <- RawUsedVars],
+ case correct_var_use(UsedVars) of
+ true ->
+ PairAll = fun(L,Y) -> [{X,{var,0,Y}} || X <- L] end,
+ VarSubsts =
+ lists:flatten(lists:zipwith(PairAll,UsedVars,VarNames)),
+ VarSubstsDict = dict:from_list(VarSubsts),
+ {ok, update_vars(Call,VarSubstsDict,true)};
+ false ->
+ error
+ end;
+ false ->
+ error
+ end.
+
+-spec no_duplicates(list()) -> boolean().
+no_duplicates(L) ->
+ length(lists:usort(L)) =:= length(L).
+
+-spec correct_var_use([[var_name() | 0]]) -> boolean().
+correct_var_use(UsedVars) ->
+ NoNonVarArgs = fun([0|_]) -> false; (_) -> true end,
+ lists:all(NoNonVarArgs, UsedVars)
+ andalso no_duplicates(lists:flatten(UsedVars)).
+
+-spec collect_vars(full_imm_type_ref(), abs_type(), [[var_name() | 0]]) ->
+ [[var_name() | 0]].
+collect_vars(FullADTRef, {paren_type,_,[Type]}, UsedVars) ->
+ collect_vars(FullADTRef, Type, UsedVars);
+collect_vars(FullADTRef, {ann_type,_,[_Var,Type]}, UsedVars) ->
+ collect_vars(FullADTRef, Type, UsedVars);
+collect_vars(_FullADTRef, {type,_,tuple,any}, UsedVars) ->
+ UsedVars;
+collect_vars({_Mod,SameName,Arity} = FullADTRef, {type,_,SameName,ArgForms},
+ UsedVars) ->
+ case length(ArgForms) =:= Arity of
+ true ->
+ VarArgs = [V || {var,_,V} <- ArgForms, V =/= '_'],
+ case length(VarArgs) =:= Arity of
+ true ->
+ AddToList = fun(X,L) -> [X | L] end,
+ lists:zipwith(AddToList, VarArgs, UsedVars);
+ false ->
+ [[0|L] || L <- UsedVars]
+ end;
+ false ->
+ multi_collect_vars(FullADTRef, ArgForms, UsedVars)
+ end;
+collect_vars(FullADTRef, {type,_,_Name,ArgForms}, UsedVars) ->
+ multi_collect_vars(FullADTRef, ArgForms, UsedVars);
+collect_vars({SameMod,SameName,_Arity} = FullADTRef,
+ {remote_type,_,[{atom,_,SameMod},{atom,_,SameName},ArgForms]},
+ UsedVars) ->
+ collect_vars(FullADTRef, {type,?anno(0),SameName,ArgForms}, UsedVars);
+collect_vars(FullADTRef, {remote_type,_,[_RemModForm,_NameForm,ArgForms]},
+ UsedVars) ->
+ multi_collect_vars(FullADTRef, ArgForms, UsedVars);
+collect_vars(_FullADTRef, _Call, UsedVars) ->
+ UsedVars.
+
+-spec multi_collect_vars(full_imm_type_ref(), [abs_type()],
+ [[var_name() | 0]]) -> [[var_name() | 0]].
+multi_collect_vars({_Mod,_Name,Arity} = FullADTRef, Forms, UsedVars) ->
+ NoUsedVars = lists:duplicate(Arity, []),
+ MoreUsedVars = [collect_vars(FullADTRef,T,NoUsedVars) || T <- Forms],
+ CombineVars = fun(L1,L2) -> lists:zipwith(fun erlang:'++'/2, L1, L2) end,
+ lists:foldl(CombineVars, UsedVars, MoreUsedVars).
+
+-ifdef(NO_MODULES_IN_OPAQUES).
+-type var_substs_dict() :: dict().
+-else.
+-type var_substs_dict() :: dict:dict(var_name(),abs_type()).
+-endif.
+-spec update_vars(abs_type(), var_substs_dict(), boolean()) -> abs_type().
+update_vars({paren_type,Line,[Type]}, VarSubstsDict, UnboundToAny) ->
+ {paren_type, Line, [update_vars(Type,VarSubstsDict,UnboundToAny)]};
+update_vars({ann_type,Line,[Var,Type]}, VarSubstsDict, UnboundToAny) ->
+ {ann_type, Line, [Var,update_vars(Type,VarSubstsDict,UnboundToAny)]};
+update_vars({var,Line,VarName} = Call, VarSubstsDict, UnboundToAny) ->
+ case dict:find(VarName, VarSubstsDict) of
+ {ok,SubstType} ->
+ SubstType;
+ error when UnboundToAny =:= false ->
+ Call;
+ error when UnboundToAny =:= true ->
+ {type,Line,any,[]}
+ end;
+update_vars({remote_type,Line,[RemModForm,NameForm,ArgForms]}, VarSubstsDict,
+ UnboundToAny) ->
+ NewArgForms = [update_vars(A,VarSubstsDict,UnboundToAny) || A <- ArgForms],
+ {remote_type, Line, [RemModForm,NameForm,NewArgForms]};
+update_vars({T,_,tuple,any} = Call, _VarSubstsDict, _UnboundToAny) when ?IS_TYPE_TAG(T) ->
+ Call;
+update_vars({T,Line,Name,ArgForms}, VarSubstsDict, UnboundToAny) when ?IS_TYPE_TAG(T) ->
+ NewArgForms = [update_vars(A,VarSubstsDict,UnboundToAny) || A <- ArgForms],
+ {T, Line, Name, NewArgForms};
+update_vars(Call, _VarSubstsDict, _UnboundToAny) ->
+ Call.
+
+
+%%------------------------------------------------------------------------------
+%% Match-related functions
+%%------------------------------------------------------------------------------
+
+-spec get_pattern(position(), [abs_type()]) -> pattern().
+get_pattern(TargetPos, FieldForms) ->
+ {0,RevPattern} = lists:foldl(fun add_field/2, {TargetPos,[]}, FieldForms),
+ list_to_tuple(lists:reverse(RevPattern)).
+
+-spec add_field(abs_type(), {non_neg_integer(),[pat_field()]}) ->
+ {non_neg_integer(),[pat_field(),...]}.
+add_field(_Type, {1,Acc}) ->
+ {0, [1|Acc]};
+add_field({atom,_,Tag}, {Left,Acc}) ->
+ {erlang:max(0,Left-1), [Tag|Acc]};
+add_field(_Type, {Left,Acc}) ->
+ {erlang:max(0,Left-1), [0|Acc]}.
+
+%% @private
+-spec match(pattern(), tuple()) -> term().
+match(Pattern, Term) when tuple_size(Pattern) =:= tuple_size(Term) ->
+ match(tuple_to_list(Pattern), tuple_to_list(Term), none, false);
+match(_Pattern, _Term) ->
+ throw(no_match).
+
+-spec match([pat_field()], [term()], 'none' | {'ok',T}, boolean()) -> T.
+match([], [], {ok,Target}, _TypeMode) ->
+ Target;
+match([0|PatRest], [_|ToMatchRest], Acc, TypeMode) ->
+ match(PatRest, ToMatchRest, Acc, TypeMode);
+match([1|PatRest], [Target|ToMatchRest], none, TypeMode) ->
+ match(PatRest, ToMatchRest, {ok,Target}, TypeMode);
+match([Tag|PatRest], [X|ToMatchRest], Acc, TypeMode) when is_atom(Tag) ->
+ MatchesTag =
+ case TypeMode of
+ true -> can_be_tag(Tag, X);
+ false -> Tag =:= X
+ end,
+ case MatchesTag of
+ true -> match(PatRest, ToMatchRest, Acc, TypeMode);
+ false -> throw(no_match)
+ end.
+
+%% CAUTION: these must be sorted
+-define(NON_ATOM_TYPES,
+ [arity,binary,bitstring,byte,char,float,'fun',function,integer,iodata,
+ iolist,list,maybe_improper_list,mfa,neg_integer,nil,no_return,
+ non_neg_integer,none,nonempty_improper_list,nonempty_list,
+ nonempty_maybe_improper_list,nonempty_string,number,pid,port,
+ pos_integer,range,record,reference,string,tuple]).
+-define(NON_TUPLE_TYPES,
+ [arity,atom,binary,bitstring,bool,boolean,byte,char,float,'fun',
+ function,identifier,integer,iodata,iolist,list,maybe_improper_list,
+ neg_integer,nil,no_return,node,non_neg_integer,none,
+ nonempty_improper_list,nonempty_list,nonempty_maybe_improper_list,
+ nonempty_string,number,pid,port,pos_integer,range,reference,string,
+ timeout]).
+-define(NO_HEAD_TYPES,
+ [arity,atom,binary,bitstring,bool,boolean,byte,char,float,'fun',
+ function,identifier,integer,mfa,module,neg_integer,nil,no_return,node,
+ non_neg_integer,none,number,pid,port,pos_integer,range,record,
+ reference,timeout,tuple]).
+
+-spec can_be_tag(atom(), abs_type()) -> boolean().
+can_be_tag(Tag, {ann_type,_,[_Var,Type]}) ->
+ can_be_tag(Tag, Type);
+can_be_tag(Tag, {paren_type,_,[Type]}) ->
+ can_be_tag(Tag, Type);
+can_be_tag(Tag, {atom,_,Atom}) ->
+ Tag =:= Atom;
+can_be_tag(_Tag, {integer,_,_Int}) ->
+ false;
+can_be_tag(_Tag, {op,_,_Op,_Arg}) ->
+ false;
+can_be_tag(_Tag, {op,_,_Op,_Arg1,_Arg2}) ->
+ false;
+can_be_tag(Tag, {type,_,BName,[]}) when BName =:= bool; BName =:= boolean ->
+ is_boolean(Tag);
+can_be_tag(Tag, {type,_,timeout,[]}) ->
+ Tag =:= infinity;
+can_be_tag(Tag, {type,_,union,Choices}) ->
+ lists:any(fun(C) -> can_be_tag(Tag,C) end, Choices);
+can_be_tag(_Tag, {type,_,Name,_Args}) ->
+ not ordsets:is_element(Name, ?NON_ATOM_TYPES);
+can_be_tag(_Tag, _Type) ->
+ true.
+
+-spec cant_match(pattern(), abs_type()) -> boolean().
+cant_match(Pattern, {ann_type,_,[_Var,Type]}) ->
+ cant_match(Pattern, Type);
+cant_match(Pattern, {paren_type,_,[Type]}) ->
+ cant_match(Pattern, Type);
+cant_match(_Pattern, {atom,_,_Atom}) ->
+ true;
+cant_match(_Pattern, {integer,_,_Int}) ->
+ true;
+cant_match(_Pattern, {op,_,_Op,_Arg}) ->
+ true;
+cant_match(_Pattern, {op,_,_Op,_Arg1,_Arg2}) ->
+ true;
+cant_match(Pattern, {type,Anno,mfa,[]}) ->
+ MFA_Ts = [{type,Anno,atom,[]}, {type,Anno,atom,[]}, {type,Anno,arity,[]}],
+ cant_match(Pattern, {type,Anno,tuple,MFA_Ts});
+cant_match(Pattern, {type,_,union,Choices}) ->
+ lists:all(fun(C) -> cant_match(Pattern,C) end, Choices);
+cant_match(_Pattern, {type,_,tuple,any}) ->
+ false;
+cant_match(Pattern, {type,_,tuple,Fields}) ->
+ tuple_size(Pattern) =/= length(Fields) orelse
+ try match(tuple_to_list(Pattern), Fields, none, true) of
+ _ -> false
+ catch
+ throw:no_match -> true
+ end;
+cant_match(_Pattern, {type,_,Name,_Args}) ->
+ ordsets:is_element(Name, ?NON_TUPLE_TYPES);
+cant_match(_Pattern, _Type) ->
+ false.
+
+-spec cant_have_head(abs_type()) -> boolean().
+cant_have_head({ann_type,_,[_Var,Type]}) ->
+ cant_have_head(Type);
+cant_have_head({paren_type,_,[Type]}) ->
+ cant_have_head(Type);
+cant_have_head({atom,_,_Atom}) ->
+ true;
+cant_have_head({integer,_,_Int}) ->
+ true;
+cant_have_head({op,_,_Op,_Arg}) ->
+ true;
+cant_have_head({op,_,_Op,_Arg1,_Arg2}) ->
+ true;
+cant_have_head({type,_,union,Choices}) ->
+ lists:all(fun cant_have_head/1, Choices);
+cant_have_head({type,_,Name,_Args}) ->
+ ordsets:is_element(Name, ?NO_HEAD_TYPES);
+cant_have_head(_Type) ->
+ false.
+
+%% Only covers atoms, integers and tuples, i.e. those that can be specified
+%% through singleton types.
+-spec term_to_singleton_type(atom() | integer()
+ | loose_tuple(atom() | integer())) -> abs_type().
+term_to_singleton_type(Atom) when is_atom(Atom) ->
+ {atom,?anno(0),Atom};
+term_to_singleton_type(Int) when is_integer(Int), Int >= 0 ->
+ {integer,?anno(0),Int};
+term_to_singleton_type(Int) when is_integer(Int), Int < 0 ->
+ A = ?anno(0),
+ {op,A,'-',{integer,A,-Int}};
+term_to_singleton_type(Tuple) when is_tuple(Tuple) ->
+ Fields = tuple_to_list(Tuple),
+ {type,?anno(0),tuple,[term_to_singleton_type(F) || F <- Fields]}.
+
+
+%%------------------------------------------------------------------------------
+%% Instance testing functions
+%%------------------------------------------------------------------------------
+
+%% CAUTION: this must be sorted
+-define(EQUIV_TYPES,
+ [{arity, {type,0,range,[{integer,0,0},{integer,0,255}]}},
+ {bool, {type,0,boolean,[]}},
+ {byte, {type,0,range,[{integer,0,0},{integer,0,255}]}},
+ {char, {type,0,range,[{integer,0,0},{integer,0,16#10ffff}]}},
+ {function, {type,0,'fun',[]}},
+ {identifier, {type,0,union,[{type,0,pid,[]},{type,0,port,[]},
+ {type,0,reference,[]}]}},
+ {iodata, {type,0,union,[{type,0,binary,[]},{type,0,iolist,[]}]}},
+ {iolist, {type,0,maybe_improper_list,
+ [{type,0,union,[{type,0,byte,[]},{type,0,binary,[]},
+ {type,0,iolist,[]}]},
+ {type,0,binary,[]}]}},
+ {list, {type,0,list,[{type,0,any,[]}]}},
+ {maybe_improper_list, {type,0,maybe_improper_list,[{type,0,any,[]},
+ {type,0,any,[]}]}},
+ {mfa, {type,0,tuple,[{type,0,atom,[]},{type,0,atom,[]},
+ {type,0,arity,[]}]}},
+ {node, {type,0,atom,[]}},
+ {nonempty_list, {type,0,nonempty_list,[{type,0,any,[]}]}},
+ {nonempty_maybe_improper_list, {type,0,nonempty_maybe_improper_list,
+ [{type,0,any,[]},{type,0,any,[]}]}},
+ {nonempty_string, {type,0,nonempty_list,[{type,0,char,[]}]}},
+ {string, {type,0,list,[{type,0,char,[]}]}},
+ {term, {type,0,any,[]}},
+ {timeout, {type,0,union,[{atom,0,infinity},
+ {type,0,non_neg_integer,[]}]}}]).
+
+%% @private
+%% TODO: Most of these functions accept an extended form of abs_type(), namely
+%% the addition of a custom wrapper: {'from_mod',mod_name(),...}
+-spec is_instance(term(), mod_name(), abs_type()) -> boolean().
+is_instance(X, Mod, TypeForm) ->
+ is_instance(X, Mod, TypeForm, []).
+
+-spec is_instance(term(), mod_name(), abs_type(), imm_stack()) -> boolean().
+is_instance(X, _Mod, {from_mod,OrigMod,Type}, Stack) ->
+ is_instance(X, OrigMod, Type, Stack);
+is_instance(_X, _Mod, {var,_,'_'}, _Stack) ->
+ true;
+is_instance(_X, _Mod, {var,_,Name}, _Stack) ->
+ %% All unconstrained spec vars have been replaced by 'any()' and we always
+ %% replace the variables on the RHS of types before recursing into them.
+ %% Provided that '-type' declarations contain no unbound variables, we
+ %% don't expect to find any non-'_' variables while recursing.
+ throw({'$typeserver',{unbound_var_in_type_declaration,Name}});
+is_instance(X, Mod, {ann_type,_,[_Var,Type]}, Stack) ->
+ is_instance(X, Mod, Type, Stack);
+is_instance(X, Mod, {paren_type,_,[Type]}, Stack) ->
+ is_instance(X, Mod, Type, Stack);
+is_instance(X, Mod, {remote_type,_,[{atom,_,RemMod},{atom,_,Name},ArgForms]},
+ Stack) ->
+ is_custom_instance(X, Mod, RemMod, Name, ArgForms, true, Stack);
+is_instance(SameAtom, _Mod, {atom,_,SameAtom}, _Stack) ->
+ true;
+is_instance(SameInt, _Mod, {integer,_,SameInt}, _Stack) ->
+ true;
+is_instance(X, _Mod, {op,_,_Op,_Arg} = Expr, _Stack) ->
+ is_int_const(X, Expr);
+is_instance(X, _Mod, {op,_,_Op,_Arg1,_Arg2} = Expr, _Stack) ->
+ is_int_const(X, Expr);
+is_instance(_X, _Mod, {type,_,any,[]}, _Stack) ->
+ true;
+is_instance(X, _Mod, {type,_,atom,[]}, _Stack) ->
+ is_atom(X);
+is_instance(X, _Mod, {type,_,binary,[]}, _Stack) ->
+ is_binary(X);
+is_instance(X, _Mod, {type,_,binary,[BaseExpr,UnitExpr]}, _Stack) ->
+ %% <<_:X,_:_*Y>> means "bitstrings of X + k*Y bits, k >= 0"
+ case eval_int(BaseExpr) of
+ {ok,Base} when Base >= 0 ->
+ case eval_int(UnitExpr) of
+ {ok,Unit} when Unit >= 0 ->
+ case is_bitstring(X) of
+ true ->
+ BitSizeX = bit_size(X),
+ case Unit =:= 0 of
+ true ->
+ BitSizeX =:= Base;
+ false ->
+ BitSizeX >= Base
+ andalso
+ (BitSizeX - Base) rem Unit =:= 0
+ end;
+ false -> false
+ end;
+ _ ->
+ abs_expr_error(invalid_unit, UnitExpr)
+ end;
+ _ ->
+ abs_expr_error(invalid_base, BaseExpr)
+ end;
+is_instance(X, _Mod, {type,_,bitstring,[]}, _Stack) ->
+ is_bitstring(X);
+is_instance(X, _Mod, {type,_,boolean,[]}, _Stack) ->
+ is_boolean(X);
+is_instance(X, _Mod, {type,_,float,[]}, _Stack) ->
+ is_float(X);
+is_instance(X, _Mod, {type,_,'fun',[]}, _Stack) ->
+ is_function(X);
+%% TODO: how to check range type? random inputs? special case for 0-arity?
+is_instance(X, _Mod, {type,_,'fun',[{type,_,any,[]},_Range]}, _Stack) ->
+ is_function(X);
+is_instance(X, _Mod, {type,_,'fun',[{type,_,product,Domain},_Range]}, _Stack) ->
+ is_function(X, length(Domain));
+is_instance(X, _Mod, {type,_,integer,[]}, _Stack) ->
+ is_integer(X);
+is_instance(X, Mod, {type,_,list,[Type]}, _Stack) ->
+ list_test(X, Mod, Type, dummy, true, true, false);
+is_instance(X, Mod, {type,_,maybe_improper_list,[Cont,Term]}, _Stack) ->
+ list_test(X, Mod, Cont, Term, true, true, true);
+is_instance(X, _Mod, {type,_,module,[]}, _Stack) ->
+ is_atom(X) orelse
+ is_tuple(X) andalso X =/= {} andalso is_atom(element(1,X));
+is_instance([], _Mod, {type,_,nil,[]}, _Stack) ->
+ true;
+is_instance(X, _Mod, {type,_,neg_integer,[]}, _Stack) ->
+ is_integer(X) andalso X < 0;
+is_instance(X, _Mod, {type,_,non_neg_integer,[]}, _Stack) ->
+ is_integer(X) andalso X >= 0;
+is_instance(X, Mod, {type,_,nonempty_list,[Type]}, _Stack) ->
+ list_test(X, Mod, Type, dummy, false, true, false);
+is_instance(X, Mod, {type,_,nonempty_improper_list,[Cont,Term]}, _Stack) ->
+ list_test(X, Mod, Cont, Term, false, false, true);
+is_instance(X, Mod, {type,_,nonempty_maybe_improper_list,[Cont,Term]},
+ _Stack) ->
+ list_test(X, Mod, Cont, Term, false, true, true);
+is_instance(X, _Mod, {type,_,number,[]}, _Stack) ->
+ is_number(X);
+is_instance(X, _Mod, {type,_,pid,[]}, _Stack) ->
+ is_pid(X);
+is_instance(X, _Mod, {type,_,port,[]}, _Stack) ->
+ is_port(X);
+is_instance(X, _Mod, {type,_,pos_integer,[]}, _Stack) ->
+ is_integer(X) andalso X > 0;
+is_instance(_X, _Mod, {type,_,product,_Elements}, _Stack) ->
+ throw({'$typeserver',{internal,product_in_is_instance}});
+is_instance(X, _Mod, {type,_,range,[LowExpr,HighExpr]}, _Stack) ->
+ case {eval_int(LowExpr),eval_int(HighExpr)} of
+ {{ok,Low},{ok,High}} when Low =< High ->
+ X >= Low andalso X =< High;
+ _ ->
+ abs_expr_error(invalid_range, LowExpr, HighExpr)
+ end;
+is_instance(X, Mod, {type,_,record,[{atom,_,Name} = NameForm | RawSubsts]},
+ Stack) ->
+ Substs = [{N,T} || {type,_,field_type,[{atom,_,N},T]} <- RawSubsts],
+ SubstsDict = dict:from_list(Substs),
+ case get_type_repr(Mod, {record,Name,0}, false) of
+ {ok,{abs_record,OrigFields}} ->
+ Fields = [case dict:find(FieldName, SubstsDict) of
+ {ok,NewFieldType} -> NewFieldType;
+ error -> OrigFieldType
+ end
+ || {FieldName,OrigFieldType} <- OrigFields],
+ is_instance(X, Mod, {type,?anno(0),tuple,[NameForm|Fields]}, Stack);
+ {error,Reason} ->
+ throw({'$typeserver',Reason})
+ end;
+is_instance(X, _Mod, {type,_,reference,[]}, _Stack) ->
+ is_reference(X);
+is_instance(X, _Mod, {type,_,tuple,any}, _Stack) ->
+ is_tuple(X);
+is_instance(X, Mod, {type,_,tuple,Fields}, _Stack) ->
+ is_tuple(X) andalso tuple_test(tuple_to_list(X), Mod, Fields);
+is_instance(X, Mod, {type,_,union,Choices}, Stack) ->
+ IsInstance = fun(Choice) -> is_instance(X,Mod,Choice,Stack) end,
+ lists:any(IsInstance, Choices);
+is_instance(X, Mod, {T,_,Name,[]}, Stack) when ?IS_TYPE_TAG(T) ->
+ case orddict:find(Name, ?EQUIV_TYPES) of
+ {ok,EquivType} ->
+ is_instance(X, Mod, EquivType, Stack);
+ error ->
+ is_maybe_hard_adt(X, Mod, Name, [], Stack)
+ end;
+is_instance(X, Mod, {T,_,Name,ArgForms}, Stack) when ?IS_TYPE_TAG(T) ->
+ is_maybe_hard_adt(X, Mod, Name, ArgForms, Stack);
+is_instance(_X, _Mod, _Type, _Stack) ->
+ false.
+
+-spec is_int_const(term(), abs_expr()) -> boolean().
+is_int_const(X, Expr) ->
+ case eval_int(Expr) of
+ {ok,Int} ->
+ X =:= Int;
+ error ->
+ abs_expr_error(invalid_int_const, Expr)
+ end.
+
+%% TODO: We implicitly add the '| []' at the termination of maybe_improper_list.
+%% TODO: We ignore a '[]' termination in improper_list.
+-spec list_test(term(), mod_name(), abs_type(), 'dummy' | abs_type(), boolean(),
+ boolean(), boolean()) -> boolean().
+list_test(X, Mod, Content, Termination, CanEmpty, CanProper, CanImproper) ->
+ is_list(X) andalso
+ list_rec(X, Mod, Content, Termination, CanEmpty, CanProper, CanImproper).
+
+-spec list_rec(term(), mod_name(), abs_type(), 'dummy' | abs_type(), boolean(),
+ boolean(), boolean()) -> boolean().
+list_rec([], _Mod, _Content, _Termination, CanEmpty, CanProper, _CanImproper) ->
+ CanEmpty andalso CanProper;
+list_rec([X | Rest], Mod, Content, Termination, _CanEmpty, CanProper,
+ CanImproper) ->
+ is_instance(X, Mod, Content, []) andalso
+ list_rec(Rest, Mod, Content, Termination, true, CanProper, CanImproper);
+list_rec(X, Mod, _Content, Termination, _CanEmpty, _CanProper, CanImproper) ->
+ CanImproper andalso is_instance(X, Mod, Termination, []).
+
+-spec tuple_test([term()], mod_name(), [abs_type()]) -> boolean().
+tuple_test([], _Mod, []) ->
+ true;
+tuple_test([X | XTail], Mod, [T | TTail]) ->
+ is_instance(X, Mod, T, []) andalso tuple_test(XTail, Mod, TTail);
+tuple_test(_, _Mod, _) ->
+ false.
+
+-spec is_maybe_hard_adt(term(), mod_name(), type_name(), [abs_type()],
+ imm_stack()) -> boolean().
+is_maybe_hard_adt(X, Mod, Name, ArgForms, Stack) ->
+ case orddict:find({Name,length(ArgForms)}, ?HARD_ADTS) of
+ {ok,ADTMod} ->
+ is_custom_instance(X, Mod, ADTMod, Name, ArgForms, true, Stack);
+ error ->
+ is_custom_instance(X, Mod, Mod, Name, ArgForms, false, Stack)
+ end.
+
+-spec is_custom_instance(term(), mod_name(), mod_name(), type_name(),
+ [abs_type()], boolean(), imm_stack()) -> boolean().
+is_custom_instance(X, Mod, RemMod, Name, RawArgForms, IsRemote, Stack) ->
+ ArgForms = case Mod =/= RemMod of
+ true -> [{from_mod,Mod,A} || A <- RawArgForms];
+ false -> RawArgForms
+ end,
+ Arity = length(ArgForms),
+ FullTypeRef = {RemMod,Name,Arity},
+ case lists:member(FullTypeRef, Stack) of
+ true ->
+ throw({'$typeserver',{self_reference,FullTypeRef}});
+ false ->
+ TypeRef = {type,Name,Arity},
+ AbsType = get_abs_type(RemMod, TypeRef, ArgForms, IsRemote),
+ is_instance(X, RemMod, AbsType, [FullTypeRef|Stack])
+ end.
+
+-spec get_abs_type(mod_name(), type_ref(), [abs_type()], boolean()) ->
+ abs_type().
+get_abs_type(RemMod, TypeRef, ArgForms, IsRemote) ->
+ case get_type_repr(RemMod, TypeRef, IsRemote) of
+ {ok,TypeRepr} ->
+ {FinalAbsType,SymbInfo,VarNames} =
+ case TypeRepr of
+ {cached,_FinType,FAT,SI} -> {FAT,SI,[]};
+ {abs_type,FAT,VN,SI} -> {FAT,SI,VN}
+ end,
+ AbsType =
+ case SymbInfo of
+ not_symb -> FinalAbsType;
+ {orig_abs,OrigAbsType} -> OrigAbsType
+ end,
+ VarSubstsDict = dict:from_list(lists:zip(VarNames,ArgForms)),
+ update_vars(AbsType, VarSubstsDict, false);
+ {error,Reason} ->
+ throw({'$typeserver',Reason})
+ end.
+
+-spec abs_expr_error(atom(), abs_expr()) -> no_return().
+abs_expr_error(ImmReason, Expr) ->
+ {error,Reason} = expr_error(ImmReason, Expr),
+ throw({'$typeserver',Reason}).
+
+-spec abs_expr_error(atom(), abs_expr(), abs_expr()) -> no_return().
+abs_expr_error(ImmReason, Expr1, Expr2) ->
+ {error,Reason} = expr_error(ImmReason, Expr1, Expr2),
+ throw({'$typeserver',Reason}).
+
+
+%%------------------------------------------------------------------------------
+%% Type translation functions
+%%------------------------------------------------------------------------------
+
+-spec convert(mod_name(), abs_type(), state()) ->
+ rich_result2(fin_type(),state()).
+convert(Mod, TypeForm, State) ->
+ case convert(Mod, TypeForm, State, [], dict:new()) of
+ {ok,{simple,Type},NewState} ->
+ {ok, Type, NewState};
+ {ok,{rec,_RecFun,_RecArgs},_NewState} ->
+ {error, {internal,rec_returned_to_toplevel}};
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec convert(mod_name(), abs_type(), state(), stack(), var_dict()) ->
+ rich_result2(ret_type(),state()).
+convert(Mod, {paren_type,_,[Type]}, State, Stack, VarDict) ->
+ convert(Mod, Type, State, Stack, VarDict);
+convert(Mod, {ann_type,_,[_Var,Type]}, State, Stack, VarDict) ->
+ convert(Mod, Type, State, Stack, VarDict);
+convert(_Mod, {var,_,'_'}, State, _Stack, _VarDict) ->
+ {ok, {simple,proper_types:any()}, State};
+convert(_Mod, {var,_,VarName}, State, _Stack, VarDict) ->
+ case dict:find(VarName, VarDict) of
+ %% TODO: do we need to check if we are at toplevel of a recursive?
+ {ok,RetType} -> {ok, RetType, State};
+ error -> {error, {unbound_var,VarName}}
+ end;
+convert(Mod, {remote_type,_,[{atom,_,RemMod},{atom,_,Name},ArgForms]}, State,
+ Stack, VarDict) ->
+ case prepare_for_remote(RemMod, Name, length(ArgForms), State) of
+ {ok,NewState} ->
+ convert_custom(Mod,RemMod,Name,ArgForms,NewState,Stack,VarDict);
+ {error,_Reason} = Error ->
+ Error
+ end;
+convert(_Mod, {atom,_,Atom}, State, _Stack, _VarDict) ->
+ {ok, {simple,proper_types:exactly(Atom)}, State};
+convert(_Mod, {integer,_,_Int} = IntExpr, State, _Stack, _VarDict) ->
+ convert_integer(IntExpr, State);
+convert(_Mod, {op,_,_Op,_Arg} = OpExpr, State, _Stack, _VarDict) ->
+ convert_integer(OpExpr, State);
+convert(_Mod, {op,_,_Op,_Arg1,_Arg2} = OpExpr, State, _Stack, _VarDict) ->
+ convert_integer(OpExpr, State);
+convert(_Mod, {type,_,binary,[BaseExpr,UnitExpr]}, State, _Stack, _VarDict) ->
+ %% <<_:X,_:_*Y>> means "bitstrings of X + k*Y bits, k >= 0"
+ case eval_int(BaseExpr) of
+ {ok,0} ->
+ case eval_int(UnitExpr) of
+ {ok,0} -> {ok, {simple,proper_types:exactly(<<>>)}, State};
+ {ok,1} -> {ok, {simple,proper_types:bitstring()}, State};
+ {ok,8} -> {ok, {simple,proper_types:binary()}, State};
+ {ok,N} when N > 0 ->
+ Gen = ?LET(L, proper_types:list(proper_types:bitstring(N)),
+ concat_bitstrings(L)),
+ {ok, {simple,Gen}, State};
+ _ -> expr_error(invalid_unit, UnitExpr)
+ end;
+ {ok,Base} when Base > 0 ->
+ Head = proper_types:bitstring(Base),
+ case eval_int(UnitExpr) of
+ {ok,0} -> {ok, {simple,Head}, State};
+ {ok,1} ->
+ Tail = proper_types:bitstring(),
+ {ok, {simple,concat_binary_gens(Head, Tail)}, State};
+ {ok,8} ->
+ Tail = proper_types:binary(),
+ {ok, {simple,concat_binary_gens(Head, Tail)}, State};
+ {ok,N} when N > 0 ->
+ Tail =
+ ?LET(L, proper_types:list(proper_types:bitstring(N)),
+ concat_bitstrings(L)),
+ {ok, {simple,concat_binary_gens(Head, Tail)}, State};
+ _ -> expr_error(invalid_unit, UnitExpr)
+ end;
+ _ ->
+ expr_error(invalid_base, BaseExpr)
+ end;
+convert(_Mod, {type,_,range,[LowExpr,HighExpr]}, State, _Stack, _VarDict) ->
+ case {eval_int(LowExpr),eval_int(HighExpr)} of
+ {{ok,Low},{ok,High}} when Low =< High ->
+ {ok, {simple,proper_types:integer(Low,High)}, State};
+ _ ->
+ expr_error(invalid_range, LowExpr, HighExpr)
+ end;
+convert(_Mod, {type,_,nil,[]}, State, _Stack, _VarDict) ->
+ {ok, {simple,proper_types:exactly([])}, State};
+convert(Mod, {type,_,list,[ElemForm]}, State, Stack, VarDict) ->
+ convert_list(Mod, false, ElemForm, State, Stack, VarDict);
+convert(Mod, {type,_,nonempty_list,[ElemForm]}, State, Stack, VarDict) ->
+ convert_list(Mod, true, ElemForm, State, Stack, VarDict);
+convert(_Mod, {type,_,nonempty_list,[]}, State, _Stack, _VarDict) ->
+ {ok, {simple,proper_types:non_empty(proper_types:list())}, State};
+convert(_Mod, {type,_,nonempty_string,[]}, State, _Stack, _VarDict) ->
+ {ok, {simple,proper_types:non_empty(proper_types:string())}, State};
+convert(_Mod, {type,_,tuple,any}, State, _Stack, _VarDict) ->
+ {ok, {simple,proper_types:tuple()}, State};
+convert(Mod, {type,_,tuple,ElemForms}, State, Stack, VarDict) ->
+ convert_tuple(Mod, ElemForms, false, State, Stack, VarDict);
+convert(Mod, {type,_,'$fixed_list',ElemForms}, State, Stack, VarDict) ->
+ convert_tuple(Mod, ElemForms, true, State, Stack, VarDict);
+convert(Mod, {type,_,record,[{atom,_,Name}|FieldForms]}, State, Stack,
+ VarDict) ->
+ convert_record(Mod, Name, FieldForms, State, Stack, VarDict);
+convert(Mod, {type,_,union,ChoiceForms}, State, Stack, VarDict) ->
+ convert_union(Mod, ChoiceForms, State, Stack, VarDict);
+convert(Mod, {type,_,'fun',[{type,_,product,Domain},Range]}, State, Stack,
+ VarDict) ->
+ convert_fun(Mod, length(Domain), Range, State, Stack, VarDict);
+%% TODO: These types should be replaced with accurate types.
+%% TODO: Add support for nonempty_improper_list/2.
+convert(Mod, {type,Anno,maybe_improper_list,[]}, State, Stack, VarDict) ->
+ convert(Mod, {type,Anno,list,[]}, State, Stack, VarDict);
+convert(Mod, {type,A,maybe_improper_list,[Cont,_Ter]}, State, Stack, VarDict) ->
+ convert(Mod, {type,A,list,[Cont]}, State, Stack, VarDict);
+convert(Mod, {type,A,nonempty_maybe_improper_list,[]}, State, Stack, VarDict) ->
+ convert(Mod, {type,A,nonempty_list,[]}, State, Stack, VarDict);
+convert(Mod, {type,A,nonempty_maybe_improper_list,[Cont,_Term]}, State, Stack,
+ VarDict) ->
+ convert(Mod, {type,A,nonempty_list,[Cont]}, State, Stack, VarDict);
+convert(Mod, {type,A,iodata,[]}, State, Stack, VarDict) ->
+ RealType = {type,A,union,[{type,A,binary,[]},{type,A,iolist,[]}]},
+ convert(Mod, RealType, State, Stack, VarDict);
+convert(Mod, {T,_,Name,[]}, State, Stack, VarDict) when ?IS_TYPE_TAG(T) ->
+ case ordsets:is_element(Name, ?STD_TYPES_0) of
+ true ->
+ {ok, {simple,proper_types:Name()}, State};
+ false ->
+ convert_maybe_hard_adt(Mod, Name, [], State, Stack, VarDict)
+ end;
+convert(Mod, {T,_,Name,ArgForms}, State, Stack, VarDict) when ?IS_TYPE_TAG(T) ->
+ convert_maybe_hard_adt(Mod, Name, ArgForms, State, Stack, VarDict);
+convert(_Mod, TypeForm, _State, _Stack, _VarDict) ->
+ {error, {unsupported_type,TypeForm}}.
+
+-spec concat_bitstrings([bitstring()]) -> bitstring().
+concat_bitstrings(BitStrings) ->
+ concat_bitstrings_tr(BitStrings, <<>>).
+
+-spec concat_bitstrings_tr([bitstring()], bitstring()) -> bitstring().
+concat_bitstrings_tr([], Acc) ->
+ Acc;
+concat_bitstrings_tr([BitString | Rest], Acc) ->
+ concat_bitstrings_tr(Rest, <<Acc/bits,BitString/bits>>).
+
+-spec concat_binary_gens(fin_type(), fin_type()) -> fin_type().
+concat_binary_gens(HeadType, TailType) ->
+ ?LET({H,T}, {HeadType,TailType}, <<H/bits,T/bits>>).
+
+-spec convert_fun(mod_name(), arity(), abs_type(), state(), stack(),
+ var_dict()) -> rich_result2(ret_type(),state()).
+convert_fun(Mod, Arity, Range, State, Stack, VarDict) ->
+ case convert(Mod, Range, State, ['fun' | Stack], VarDict) of
+ {ok,{simple,RangeType},NewState} ->
+ {ok, {simple,proper_types:function(Arity,RangeType)}, NewState};
+ {ok,{rec,RecFun,RecArgs},NewState} ->
+ case at_toplevel(RecArgs, Stack) of
+ true -> base_case_error(Stack);
+ false -> convert_rec_fun(Arity, RecFun, RecArgs, NewState)
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec convert_rec_fun(arity(), rec_fun(), rec_args(), state()) ->
+ {'ok',ret_type(),state()}.
+convert_rec_fun(Arity, RecFun, RecArgs, State) ->
+ %% We bind the generated value by size.
+ NewRecFun =
+ fun(GenFuns,Size) ->
+ proper_types:function(Arity, RecFun(GenFuns,Size))
+ end,
+ NewRecArgs = clean_rec_args(RecArgs),
+ {ok, {rec,NewRecFun,NewRecArgs}, State}.
+
+-spec convert_list(mod_name(), boolean(), abs_type(), state(), stack(),
+ var_dict()) -> rich_result2(ret_type(),state()).
+convert_list(Mod, NonEmpty, ElemForm, State, Stack, VarDict) ->
+ case convert(Mod, ElemForm, State, [list | Stack], VarDict) of
+ {ok,{simple,ElemType},NewState} ->
+ InnerType = proper_types:list(ElemType),
+ FinType = case NonEmpty of
+ true -> proper_types:non_empty(InnerType);
+ false -> InnerType
+ end,
+ {ok, {simple,FinType}, NewState};
+ {ok,{rec,RecFun,RecArgs},NewState} ->
+ case {at_toplevel(RecArgs,Stack), NonEmpty} of
+ {true,true} ->
+ base_case_error(Stack);
+ {true,false} ->
+ NewRecFun =
+ fun(GenFuns,Size) ->
+ ElemGen = fun(S) -> ?LAZY(RecFun(GenFuns,S)) end,
+ proper_types:distlist(Size, ElemGen, false)
+ end,
+ NewRecArgs = clean_rec_args(RecArgs),
+ {ok, {rec,NewRecFun,NewRecArgs}, NewState};
+ {false,_} ->
+ {NewRecFun,NewRecArgs} =
+ convert_rec_list(RecFun, RecArgs, NonEmpty),
+ {ok, {rec,NewRecFun,NewRecArgs}, NewState}
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec convert_rec_list(rec_fun(), rec_args(), boolean()) ->
+ {rec_fun(),rec_args()}.
+convert_rec_list(RecFun, [{true,FullTypeRef}] = RecArgs, NonEmpty) ->
+ {NewRecFun,_NormalRecArgs} =
+ convert_normal_rec_list(RecFun, RecArgs, NonEmpty),
+ AltRecFun =
+ fun([InstListGen],Size) ->
+ InstTypesList =
+ proper_types:get_prop(internal_types, InstListGen(Size)),
+ proper_types:fixed_list([RecFun([fun(_Size) -> I end],0)
+ || I <- InstTypesList])
+ end,
+ NewRecArgs = [{{list,NonEmpty,AltRecFun},FullTypeRef}],
+ {NewRecFun, NewRecArgs};
+convert_rec_list(RecFun, RecArgs, NonEmpty) ->
+ convert_normal_rec_list(RecFun, RecArgs, NonEmpty).
+
+-spec convert_normal_rec_list(rec_fun(), rec_args(), boolean()) ->
+ {rec_fun(),rec_args()}.
+convert_normal_rec_list(RecFun, RecArgs, NonEmpty) ->
+ NewRecFun = fun(GenFuns,Size) ->
+ ElemGen = fun(S) -> RecFun(GenFuns, S) end,
+ proper_types:distlist(Size, ElemGen, NonEmpty)
+ end,
+ NewRecArgs = clean_rec_args(RecArgs),
+ {NewRecFun, NewRecArgs}.
+
+-spec convert_tuple(mod_name(), [abs_type()], boolean(), state(), stack(),
+ var_dict()) -> rich_result2(ret_type(),state()).
+convert_tuple(Mod, ElemForms, ToList, State, Stack, VarDict) ->
+ case process_list(Mod, ElemForms, State, [tuple | Stack], VarDict) of
+ {ok,RetTypes,NewState} ->
+ case combine_ret_types(RetTypes, {tuple,ToList}) of
+ {simple,_FinType} = RetType ->
+ {ok, RetType, NewState};
+ {rec,_RecFun,RecArgs} = RetType ->
+ case at_toplevel(RecArgs, Stack) of
+ true -> base_case_error(Stack);
+ false -> {ok, RetType, NewState}
+ end
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec convert_union(mod_name(), [abs_type()], state(), stack(), var_dict()) ->
+ rich_result2(ret_type(),state()).
+convert_union(Mod, ChoiceForms, State, Stack, VarDict) ->
+ case process_list(Mod, ChoiceForms, State, [union | Stack], VarDict) of
+ {ok,RawChoices,NewState} ->
+ ProcessChoice = fun(T,A) -> process_choice(T,A,Stack) end,
+ {RevSelfRecs,RevNonSelfRecs,RevNonRecs} =
+ lists:foldl(ProcessChoice, {[],[],[]}, RawChoices),
+ case {lists:reverse(RevSelfRecs),lists:reverse(RevNonSelfRecs),
+ lists:reverse(RevNonRecs)} of
+ {_SelfRecs,[],[]} ->
+ base_case_error(Stack);
+ {[],NonSelfRecs,NonRecs} ->
+ {ok, combine_ret_types(NonRecs ++ NonSelfRecs, union),
+ NewState};
+ {SelfRecs,NonSelfRecs,NonRecs} ->
+ {BCaseRecFun,BCaseRecArgs} =
+ case combine_ret_types(NonRecs ++ NonSelfRecs, union) of
+ {simple,BCaseType} ->
+ {fun([],_Size) -> BCaseType end,[]};
+ {rec,BCRecFun,BCRecArgs} ->
+ {BCRecFun,BCRecArgs}
+ end,
+ NumBCaseGens = length(BCaseRecArgs),
+ [ParentRef | _Upper] = Stack,
+ FallbackRecFun = fun([SelfGen],_Size) -> SelfGen(0) end,
+ FallbackRecArgs = [{false,ParentRef}],
+ FallbackRetType = {rec,FallbackRecFun,FallbackRecArgs},
+ {rec,RCaseRecFun,RCaseRecArgs} =
+ combine_ret_types([FallbackRetType] ++ SelfRecs
+ ++ NonSelfRecs, wunion),
+ NewRecFun =
+ fun(AllGens,Size) ->
+ {BCaseGens,RCaseGens} =
+ lists:split(NumBCaseGens, AllGens),
+ case Size of
+ 0 -> BCaseRecFun(BCaseGens,0);
+ _ -> RCaseRecFun(RCaseGens,Size)
+ end
+ end,
+ NewRecArgs = BCaseRecArgs ++ RCaseRecArgs,
+ {ok, {rec,NewRecFun,NewRecArgs}, NewState}
+ end;
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec process_choice(ret_type(), {[ret_type()],[ret_type()],[ret_type()]},
+ stack()) -> {[ret_type()],[ret_type()],[ret_type()]}.
+process_choice({simple,_} = RetType, {SelfRecs,NonSelfRecs,NonRecs}, _Stack) ->
+ {SelfRecs, NonSelfRecs, [RetType | NonRecs]};
+process_choice({rec,RecFun,RecArgs}, {SelfRecs,NonSelfRecs,NonRecs}, Stack) ->
+ case at_toplevel(RecArgs, Stack) of
+ true ->
+ case partition_by_toplevel(RecArgs, Stack, true) of
+ {[],[],_,_} ->
+ NewRecArgs = clean_rec_args(RecArgs),
+ {[{rec,RecFun,NewRecArgs} | SelfRecs], NonSelfRecs,
+ NonRecs};
+ {SelfRecArgs,SelfPos,OtherRecArgs,_OtherPos} ->
+ NumInstances = length(SelfRecArgs),
+ IsListInst = fun({true,_FTRef}) -> false
+ ; ({{list,_NE,_AltRecFun},_FTRef}) -> true
+ end,
+ NewRecFun =
+ case proper_arith:filter(IsListInst,SelfRecArgs) of
+ {[],[]} ->
+ no_list_inst_rec_fun(RecFun,NumInstances,
+ SelfPos);
+ {[{{list,NonEmpty,AltRecFun},_}],[ListInstPos]} ->
+ list_inst_rec_fun(AltRecFun,NumInstances,
+ SelfPos,NonEmpty,ListInstPos)
+ end,
+ [{_B,SelfRef} | _] = SelfRecArgs,
+ NewRecArgs =
+ [{false,SelfRef} | clean_rec_args(OtherRecArgs)],
+ {[{rec,NewRecFun,NewRecArgs} | SelfRecs], NonSelfRecs,
+ NonRecs}
+ end;
+ false ->
+ NewRecArgs = clean_rec_args(RecArgs),
+ {SelfRecs, [{rec,RecFun,NewRecArgs} | NonSelfRecs], NonRecs}
+ end.
+
+-spec no_list_inst_rec_fun(rec_fun(), pos_integer(), [position()]) -> rec_fun().
+no_list_inst_rec_fun(RecFun, NumInstances, SelfPos) ->
+ fun([SelfGen|OtherGens], Size) ->
+ ?LETSHRINK(
+ Instances,
+ %% Size distribution will be a little off if both normal and
+ %% instance-accepting generators are present.
+ lists:duplicate(NumInstances, SelfGen(Size div NumInstances)),
+ begin
+ InstGens = [fun(_Size) -> proper_types:exactly(I) end
+ || I <- Instances],
+ AllGens = proper_arith:insert(InstGens, SelfPos, OtherGens),
+ RecFun(AllGens, Size)
+ end)
+ end.
+
+-spec list_inst_rec_fun(rec_fun(), pos_integer(), [position()], boolean(),
+ position()) -> rec_fun().
+list_inst_rec_fun(AltRecFun, NumInstances, SelfPos, NonEmpty, ListInstPos) ->
+ fun([SelfGen|OtherGens], Size) ->
+ ?LETSHRINK(
+ AllInsts,
+ lists:duplicate(NumInstances - 1, SelfGen(Size div NumInstances))
+ ++ proper_types:distlist(Size div NumInstances, SelfGen, NonEmpty),
+ begin
+ {Instances,InstList} = lists:split(NumInstances - 1, AllInsts),
+ InstGens = [fun(_Size) -> proper_types:exactly(I) end
+ || I <- Instances],
+ InstTypesList = [proper_types:exactly(I) || I <- InstList],
+ InstListGen =
+ fun(_Size) -> proper_types:fixed_list(InstTypesList) end,
+ AllInstGens = proper_arith:list_insert(ListInstPos, InstListGen,
+ InstGens),
+ AllGens = proper_arith:insert(AllInstGens, SelfPos, OtherGens),
+ AltRecFun(AllGens, Size)
+ end)
+ end.
+
+-spec convert_maybe_hard_adt(mod_name(), type_name(), [abs_type()], state(),
+ stack(), var_dict()) ->
+ rich_result2(ret_type(),state()).
+convert_maybe_hard_adt(Mod, Name, ArgForms, State, Stack, VarDict) ->
+ Arity = length(ArgForms),
+ case orddict:find({Name,Arity}, ?HARD_ADTS) of
+ {ok,Mod} ->
+ convert_custom(Mod, Mod, Name, ArgForms, State, Stack, VarDict);
+ {ok,ADTMod} ->
+ A = ?anno(0),
+ ADT = {remote_type,A,[{atom,A,ADTMod},{atom,A,Name},ArgForms]},
+ convert(Mod, ADT, State, Stack, VarDict);
+ error ->
+ convert_custom(Mod, Mod, Name, ArgForms, State, Stack, VarDict)
+ end.
+
+-spec convert_custom(mod_name(), mod_name(), type_name(), [abs_type()], state(),
+ stack(), var_dict()) -> rich_result2(ret_type(),state()).
+convert_custom(Mod, RemMod, Name, ArgForms, State, Stack, VarDict) ->
+ case process_list(Mod, ArgForms, State, Stack, VarDict) of
+ {ok,Args,NewState} ->
+ Arity = length(Args),
+ TypeRef = {type,Name,Arity},
+ FullTypeRef = {RemMod,type,Name,Args},
+ convert_type(TypeRef, FullTypeRef, NewState, Stack);
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec convert_record(mod_name(), type_name(), [abs_type()], state(), stack(),
+ var_dict()) -> rich_result2(ret_type(),state()).
+convert_record(Mod, Name, RawSubsts, State, Stack, VarDict) ->
+ Substs = [{N,T} || {type,_,field_type,[{atom,_,N},T]} <- RawSubsts],
+ {SubstFields,SubstTypeForms} = lists:unzip(Substs),
+ case process_list(Mod, SubstTypeForms, State, Stack, VarDict) of
+ {ok,SubstTypes,NewState} ->
+ SubstsDict = dict:from_list(lists:zip(SubstFields, SubstTypes)),
+ TypeRef = {record,Name,0},
+ FullTypeRef = {Mod,record,Name,SubstsDict},
+ convert_type(TypeRef, FullTypeRef, NewState, Stack);
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec convert_type(type_ref(), full_type_ref(), state(), stack()) ->
+ rich_result2(ret_type(),state()).
+convert_type(TypeRef, {Mod,_Kind,_Name,_Spec} = FullTypeRef, State, Stack) ->
+ case stack_position(FullTypeRef, Stack) of
+ none ->
+ case get_type_repr(Mod, TypeRef, false, State) of
+ {ok,TypeRepr,NewState} ->
+ convert_new_type(TypeRef, FullTypeRef, TypeRepr, NewState,
+ Stack);
+ {error,_Reason} = Error ->
+ Error
+ end;
+ 1 ->
+ base_case_error(Stack);
+ _Pos ->
+ {ok, {rec,fun([Gen],Size) -> Gen(Size) end,[{true,FullTypeRef}]},
+ State}
+ end.
+
+-spec convert_new_type(type_ref(), full_type_ref(), type_repr(), state(),
+ stack()) -> rich_result2(ret_type(),state()).
+convert_new_type(_TypeRef, {_Mod,type,_Name,[]},
+ {cached,FinType,_TypeForm,_SymbInfo}, State, _Stack) ->
+ {ok, {simple,FinType}, State};
+convert_new_type(TypeRef, {Mod,type,_Name,Args} = FullTypeRef,
+ {abs_type,TypeForm,Vars,SymbInfo}, State, Stack) ->
+ VarDict = dict:from_list(lists:zip(Vars, Args)),
+ case convert(Mod, TypeForm, State, [FullTypeRef | Stack], VarDict) of
+ {ok, {simple,ImmFinType}, NewState} ->
+ FinType = case SymbInfo of
+ not_symb ->
+ ImmFinType;
+ {orig_abs,_OrigAbsType} ->
+ proper_symb:internal_well_defined(ImmFinType)
+ end,
+ FinalState = case Vars of
+ [] -> cache_type(Mod, TypeRef, FinType, TypeForm,
+ SymbInfo, NewState);
+ _ -> NewState
+ end,
+ {ok, {simple,FinType}, FinalState};
+ {ok, {rec,RecFun,RecArgs}, NewState} ->
+ convert_maybe_rec(FullTypeRef, SymbInfo, RecFun, RecArgs, NewState,
+ Stack);
+ {error,_Reason} = Error ->
+ Error
+ end;
+convert_new_type(_TypeRef, {Mod,record,Name,SubstsDict} = FullTypeRef,
+ {abs_record,OrigFields}, State, Stack) ->
+ Fields = [case dict:find(FieldName, SubstsDict) of
+ {ok,NewFieldType} -> NewFieldType;
+ error -> OrigFieldType
+ end
+ || {FieldName,OrigFieldType} <- OrigFields],
+ case convert_tuple(Mod, [{atom,0,Name} | Fields], false, State,
+ [FullTypeRef | Stack], dict:new()) of
+ {ok, {simple,_FinType}, _NewState} = Result ->
+ Result;
+ {ok, {rec,RecFun,RecArgs}, NewState} ->
+ convert_maybe_rec(FullTypeRef, not_symb, RecFun, RecArgs, NewState,
+ Stack);
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec cache_type(mod_name(), type_ref(), fin_type(), abs_type(), symb_info(),
+ state()) -> state().
+cache_type(Mod, TypeRef, FinType, TypeForm, SymbInfo,
+ #state{types = Types} = State) ->
+ TypeRepr = {cached,FinType,TypeForm,SymbInfo},
+ ModTypes = dict:fetch(Mod, Types),
+ NewModTypes = dict:store(TypeRef, TypeRepr, ModTypes),
+ NewTypes = dict:store(Mod, NewModTypes, Types),
+ State#state{types = NewTypes}.
+
+-spec convert_maybe_rec(full_type_ref(), symb_info(), rec_fun(), rec_args(),
+ state(), stack()) -> rich_result2(ret_type(),state()).
+convert_maybe_rec(FullTypeRef, SymbInfo, RecFun, RecArgs, State, Stack) ->
+ case at_toplevel(RecArgs, Stack) of
+ true -> base_case_error(Stack);
+ false -> safe_convert_maybe_rec(FullTypeRef, SymbInfo, RecFun, RecArgs,
+ State)
+ end.
+
+-spec safe_convert_maybe_rec(full_type_ref(),symb_info(),rec_fun(),rec_args(),
+ state()) -> rich_result2(ret_type(),state()).
+safe_convert_maybe_rec(FullTypeRef, SymbInfo, RecFun, RecArgs, State) ->
+ case partition_rec_args(FullTypeRef, RecArgs, false) of
+ {[],[],_,_} ->
+ {ok, {rec,RecFun,RecArgs}, State};
+ {MyRecArgs,MyPos,OtherRecArgs,_OtherPos} ->
+ case lists:all(fun({B,_T}) -> B =:= false end, MyRecArgs) of
+ true -> convert_rec_type(SymbInfo, RecFun, MyPos, OtherRecArgs,
+ State);
+ false -> {error, {internal,true_rec_arg_reached_type}}
+ end
+ end.
+
+-spec convert_rec_type(symb_info(), rec_fun(), [position()], rec_args(),
+ state()) -> {ok, ret_type(), state()}.
+convert_rec_type(SymbInfo, RecFun, MyPos, [], State) ->
+ NumRecArgs = length(MyPos),
+ M = fun(GenFun) ->
+ fun(Size) ->
+ GenFuns = lists:duplicate(NumRecArgs, GenFun),
+ RecFun(GenFuns, erlang:max(0,Size - 1))
+ end
+ end,
+ SizedGen = y(M),
+ ImmFinType = ?SIZED(Size,SizedGen(Size + 1)),
+ FinType = case SymbInfo of
+ not_symb ->
+ ImmFinType;
+ {orig_abs,_OrigAbsType} ->
+ proper_symb:internal_well_defined(ImmFinType)
+ end,
+ {ok, {simple,FinType}, State};
+convert_rec_type(_SymbInfo, RecFun, MyPos, OtherRecArgs, State) ->
+ NumRecArgs = length(MyPos),
+ NewRecFun =
+ fun(OtherGens,TopSize) ->
+ M = fun(GenFun) ->
+ fun(Size) ->
+ GenFuns = lists:duplicate(NumRecArgs, GenFun),
+ AllGens =
+ proper_arith:insert(GenFuns, MyPos, OtherGens),
+ RecFun(AllGens, erlang:max(0,Size - 1))
+ end
+ end,
+ (y(M))(TopSize)
+ end,
+ NewRecArgs = clean_rec_args(OtherRecArgs),
+ {ok, {rec,NewRecFun,NewRecArgs}, State}.
+
+%% Y Combinator: Read more at http://bc.tech.coop/blog/070611.html.
+-spec y(fun((fun((T) -> S)) -> fun((T) -> S))) -> fun((T) -> S).
+y(M) ->
+ G = fun(F) ->
+ M(fun(A) -> (F(F))(A) end)
+ end,
+ G(G).
+
+-spec process_list(mod_name(), [abs_type() | ret_type()], state(), stack(),
+ var_dict()) -> rich_result2([ret_type()],state()).
+process_list(Mod, RawTypes, State, Stack, VarDict) ->
+ Process = fun({simple,_FinType} = Type, {ok,Types,State1}) ->
+ {ok, [Type|Types], State1};
+ ({rec,_RecFun,_RecArgs} = Type, {ok,Types,State1}) ->
+ {ok, [Type|Types], State1};
+ (TypeForm, {ok,Types,State1}) ->
+ case convert(Mod, TypeForm, State1, Stack, VarDict) of
+ {ok,Type,State2} -> {ok,[Type|Types],State2};
+ {error,_} = Err -> Err
+ end;
+ (_RawType, {error,_} = Err) ->
+ Err
+ end,
+ case lists:foldl(Process, {ok,[],State}, RawTypes) of
+ {ok,RevTypes,NewState} ->
+ {ok, lists:reverse(RevTypes), NewState};
+ {error,_Reason} = Error ->
+ Error
+ end.
+
+-spec convert_integer(abs_expr(), state()) -> rich_result2(ret_type(),state()).
+convert_integer(Expr, State) ->
+ case eval_int(Expr) of
+ {ok,Int} -> {ok, {simple,proper_types:exactly(Int)}, State};
+ error -> expr_error(invalid_int_const, Expr)
+ end.
+
+-spec eval_int(abs_expr()) -> tagged_result(integer()).
+eval_int(Expr) ->
+ NoBindings = erl_eval:new_bindings(),
+ try erl_eval:expr(Expr, NoBindings) of
+ {value,Value,_NewBindings} when is_integer(Value) ->
+ {ok, Value};
+ _ ->
+ error
+ catch
+ error:_ ->
+ error
+ end.
+
+-spec expr_error(atom(), abs_expr()) -> {'error',term()}.
+expr_error(Reason, Expr) ->
+ {error, {Reason,lists:flatten(erl_pp:expr(Expr))}}.
+
+-spec expr_error(atom(), abs_expr(), abs_expr()) -> {'error',term()}.
+expr_error(Reason, Expr1, Expr2) ->
+ Str1 = lists:flatten(erl_pp:expr(Expr1)),
+ Str2 = lists:flatten(erl_pp:expr(Expr2)),
+ {error, {Reason,Str1,Str2}}.
+
+-spec base_case_error(stack()) -> {'error',term()}.
+%% TODO: This might confuse, since it doesn't record the arguments to parametric
+%% types or the type subsitutions of a record.
+base_case_error([{Mod,type,Name,Args} | _Upper]) ->
+ Arity = length(Args),
+ {error, {no_base_case,{Mod,type,Name,Arity}}};
+base_case_error([{Mod,record,Name,_SubstsDict} | _Upper]) ->
+ {error, {no_base_case,{Mod,record,Name}}}.
+
+
+%%------------------------------------------------------------------------------
+%% Helper datatypes handling functions
+%%------------------------------------------------------------------------------
+
+-spec stack_position(full_type_ref(), stack()) -> 'none' | pos_integer().
+stack_position(FullTypeRef, Stack) ->
+ SameType = fun(A) -> same_full_type_ref(A,FullTypeRef) end,
+ case proper_arith:find_first(SameType, Stack) of
+ {Pos,_} -> Pos;
+ none -> none
+ end.
+
+-spec partition_by_toplevel(rec_args(), stack(), boolean()) ->
+ {rec_args(),[position()],rec_args(),[position()]}.
+partition_by_toplevel(RecArgs, [], _OnlyInstanceAccepting) ->
+ {[],[],RecArgs,lists:seq(1,length(RecArgs))};
+partition_by_toplevel(RecArgs, [_Parent | _Upper], _OnlyInstanceAccepting)
+ when is_atom(_Parent) ->
+ {[],[],RecArgs,lists:seq(1,length(RecArgs))};
+partition_by_toplevel(RecArgs, [Parent | _Upper], OnlyInstanceAccepting) ->
+ partition_rec_args(Parent, RecArgs, OnlyInstanceAccepting).
+
+-spec at_toplevel(rec_args(), stack()) -> boolean().
+at_toplevel(RecArgs, Stack) ->
+ case partition_by_toplevel(RecArgs, Stack, false) of
+ {[],[],_,_} -> false;
+ _ -> true
+ end.
+
+-spec partition_rec_args(full_type_ref(), rec_args(), boolean()) ->
+ {rec_args(),[position()],rec_args(),[position()]}.
+partition_rec_args(FullTypeRef, RecArgs, OnlyInstanceAccepting) ->
+ SameType =
+ case OnlyInstanceAccepting of
+ true -> fun({false,_T}) -> false
+ ; ({_B,T}) -> same_full_type_ref(T,FullTypeRef) end;
+ false -> fun({_B,T}) -> same_full_type_ref(T,FullTypeRef) end
+ end,
+ proper_arith:partition(SameType, RecArgs).
+
+%% Tuples can be of 0 arity, unions of 1 and wunions at least of 2.
+-spec combine_ret_types([ret_type()], {'tuple',boolean()} | 'union'
+ | 'wunion') -> ret_type().
+combine_ret_types(RetTypes, EnclosingType) ->
+ case lists:all(fun is_simple_ret_type/1, RetTypes) of
+ true ->
+ %% This should never happen for wunion.
+ Combine = case EnclosingType of
+ {tuple,false} -> fun proper_types:tuple/1;
+ {tuple,true} -> fun proper_types:fixed_list/1;
+ union -> fun proper_types:union/1
+ end,
+ FinTypes = [T || {simple,T} <- RetTypes],
+ {simple, Combine(FinTypes)};
+ false ->
+ NumTypes = length(RetTypes),
+ {RevRecFuns,RevRecArgsList,NumRecs} =
+ lists:foldl(fun add_ret_type/2, {[],[],0}, RetTypes),
+ RecFuns = lists:reverse(RevRecFuns),
+ RecArgsList = lists:reverse(RevRecArgsList),
+ RecArgLens = [length(RecArgs) || RecArgs <- RecArgsList],
+ RecFunInfo = {NumTypes,NumRecs,RecArgLens,RecFuns},
+ FlatRecArgs = lists:flatten(RecArgsList),
+ {NewRecFun,NewRecArgs} =
+ case EnclosingType of
+ {tuple,ToList} ->
+ {tuple_rec_fun(RecFunInfo,ToList),
+ soft_clean_rec_args(FlatRecArgs,RecFunInfo,ToList)};
+ union ->
+ {union_rec_fun(RecFunInfo),clean_rec_args(FlatRecArgs)};
+ wunion ->
+ {wunion_rec_fun(RecFunInfo),
+ clean_rec_args(FlatRecArgs)}
+ end,
+ {rec, NewRecFun, NewRecArgs}
+ end.
+
+-spec tuple_rec_fun(rec_fun_info(), boolean()) -> rec_fun().
+tuple_rec_fun({_NumTypes,NumRecs,RecArgLens,RecFuns}, ToList) ->
+ Combine = case ToList of
+ true -> fun proper_types:fixed_list/1;
+ false -> fun proper_types:tuple/1
+ end,
+ fun(AllGFs,TopSize) ->
+ Size = TopSize div NumRecs,
+ GFsList = proper_arith:unflatten(AllGFs, RecArgLens),
+ ArgsList = [[GenFuns,Size] || GenFuns <- GFsList],
+ ZipFun = fun erlang:apply/2,
+ Combine(lists:zipwith(ZipFun, RecFuns, ArgsList))
+ end.
+
+-spec union_rec_fun(rec_fun_info()) -> rec_fun().
+union_rec_fun({_NumTypes,_NumRecs,RecArgLens,RecFuns}) ->
+ fun(AllGFs,Size) ->
+ GFsList = proper_arith:unflatten(AllGFs, RecArgLens),
+ ArgsList = [[GenFuns,Size] || GenFuns <- GFsList],
+ ZipFun = fun(F,A) -> ?LAZY(apply(F,A)) end,
+ proper_types:union(lists:zipwith(ZipFun, RecFuns, ArgsList))
+ end.
+
+-spec wunion_rec_fun(rec_fun_info()) -> rec_fun().
+wunion_rec_fun({NumTypes,_NumRecs,RecArgLens,RecFuns}) ->
+ fun(AllGFs,Size) ->
+ GFsList = proper_arith:unflatten(AllGFs, RecArgLens),
+ ArgsList = [[GenFuns,Size] || GenFuns <- GFsList],
+ ZipFun = fun(W,F,A) -> {W,?LAZY(apply(F,A))} end,
+ RecWeight = erlang:max(1, Size div (NumTypes - 1)),
+ Weights = [1 | lists:duplicate(NumTypes - 1, RecWeight)],
+ WeightedChoices = lists:zipwith3(ZipFun, Weights, RecFuns, ArgsList),
+ proper_types:wunion(WeightedChoices)
+ end.
+
+-spec add_ret_type(ret_type(), {[rec_fun()],[rec_args()],non_neg_integer()}) ->
+ {[rec_fun()],[rec_args()],non_neg_integer()}.
+add_ret_type({simple,FinType}, {RecFuns,RecArgsList,NumRecs}) ->
+ {[fun([],_) -> FinType end | RecFuns], [[] | RecArgsList], NumRecs};
+add_ret_type({rec,RecFun,RecArgs}, {RecFuns,RecArgsList,NumRecs}) ->
+ {[RecFun | RecFuns], [RecArgs | RecArgsList], NumRecs + 1}.
+
+-spec is_simple_ret_type(ret_type()) -> boolean().
+is_simple_ret_type({simple,_FinType}) ->
+ true;
+is_simple_ret_type({rec,_RecFun,_RecArgs}) ->
+ false.
+
+-spec clean_rec_args(rec_args()) -> rec_args().
+clean_rec_args(RecArgs) ->
+ [{false,F} || {_B,F} <- RecArgs].
+
+-spec soft_clean_rec_args(rec_args(), rec_fun_info(), boolean()) -> rec_args().
+soft_clean_rec_args(RecArgs, RecFunInfo, ToList) ->
+ soft_clean_rec_args_tr(RecArgs, [], RecFunInfo, ToList, false, 1).
+
+-spec soft_clean_rec_args_tr(rec_args(), rec_args(), rec_fun_info(), boolean(),
+ boolean(), position()) -> rec_args().
+soft_clean_rec_args_tr([], Acc, _RecFunInfo, _ToList, _FoundListInst, _Pos) ->
+ lists:reverse(Acc);
+soft_clean_rec_args_tr([{{list,_NonEmpty,_AltRecFun},FTRef} | Rest], Acc,
+ RecFunInfo, ToList, true, Pos) ->
+ NewArg = {false,FTRef},
+ soft_clean_rec_args_tr(Rest, [NewArg|Acc], RecFunInfo, ToList, true, Pos+1);
+soft_clean_rec_args_tr([{{list,NonEmpty,AltRecFun},FTRef} | Rest], Acc,
+ RecFunInfo, ToList, false, Pos) ->
+ {NumTypes,NumRecs,RecArgLens,RecFuns} = RecFunInfo,
+ AltRecFunPos = get_group(Pos, RecArgLens),
+ AltRecFuns = proper_arith:list_update(AltRecFunPos, AltRecFun, RecFuns),
+ AltRecFunInfo = {NumTypes,NumRecs,RecArgLens,AltRecFuns},
+ NewArg = {{list,NonEmpty,tuple_rec_fun(AltRecFunInfo,ToList)},FTRef},
+ soft_clean_rec_args_tr(Rest, [NewArg|Acc], RecFunInfo, ToList, true, Pos+1);
+soft_clean_rec_args_tr([Arg | Rest], Acc, RecFunInfo, ToList, FoundListInst,
+ Pos) ->
+ soft_clean_rec_args_tr(Rest, [Arg | Acc], RecFunInfo, ToList, FoundListInst,
+ Pos+1).
+
+-spec get_group(pos_integer(), [non_neg_integer()]) -> pos_integer().
+get_group(Pos, AllMembers) ->
+ get_group_tr(Pos, AllMembers, 1).
+
+-spec get_group_tr(pos_integer(), [non_neg_integer()], pos_integer()) ->
+ pos_integer().
+get_group_tr(Pos, [Members | Rest], GroupNum) ->
+ case Pos =< Members of
+ true -> GroupNum;
+ false -> get_group_tr(Pos - Members, Rest, GroupNum + 1)
+ end.
+
+-spec same_full_type_ref(full_type_ref(), term()) -> boolean().
+same_full_type_ref({SameMod,type,SameName,Args1},
+ {SameMod,type,SameName,Args2}) ->
+ length(Args1) =:= length(Args2)
+ andalso lists:all(fun({A,B}) -> same_ret_type(A,B) end,
+ lists:zip(Args1, Args2));
+same_full_type_ref({SameMod,record,SameName,SubstsDict1},
+ {SameMod,record,SameName,SubstsDict2}) ->
+ same_substs_dict(SubstsDict1, SubstsDict2);
+same_full_type_ref(_, _) ->
+ false.
+
+-spec same_ret_type(ret_type(), ret_type()) -> boolean().
+same_ret_type({simple,FinType1}, {simple,FinType2}) ->
+ same_fin_type(FinType1, FinType2);
+same_ret_type({rec,RecFun1,RecArgs1}, {rec,RecFun2,RecArgs2}) ->
+ NumRecArgs = length(RecArgs1),
+ length(RecArgs2) =:= NumRecArgs
+ andalso lists:all(fun({A1,A2}) -> same_rec_arg(A1,A2,NumRecArgs) end,
+ lists:zip(RecArgs1,RecArgs2))
+ andalso same_rec_fun(RecFun1, RecFun2, NumRecArgs);
+same_ret_type(_, _) ->
+ false.
+
+%% TODO: Is this too strict?
+-spec same_rec_arg(rec_arg(), rec_arg(), arity()) -> boolean().
+same_rec_arg({{list,SameBool,AltRecFun1},FTRef1},
+ {{list,SameBool,AltRecFun2},FTRef2}, NumRecArgs) ->
+ same_rec_fun(AltRecFun1, AltRecFun2, NumRecArgs)
+ andalso same_full_type_ref(FTRef1, FTRef2);
+same_rec_arg({true,FTRef1}, {true,FTRef2}, _NumRecArgs) ->
+ same_full_type_ref(FTRef1, FTRef2);
+same_rec_arg({false,FTRef1}, {false,FTRef2}, _NumRecArgs) ->
+ same_full_type_ref(FTRef1, FTRef2);
+same_rec_arg(_, _, _NumRecArgs) ->
+ false.
+
+-spec same_substs_dict(substs_dict(), substs_dict()) -> boolean().
+same_substs_dict(SubstsDict1, SubstsDict2) ->
+ SameKVPair = fun({{_K,V1},{_K,V2}}) -> same_ret_type(V1,V2);
+ (_) -> false
+ end,
+ SubstsKVList1 = lists:sort(dict:to_list(SubstsDict1)),
+ SubstsKVList2 = lists:sort(dict:to_list(SubstsDict2)),
+ length(SubstsKVList1) =:= length(SubstsKVList2)
+ andalso lists:all(SameKVPair, lists:zip(SubstsKVList1,SubstsKVList2)).
+
+-spec same_fin_type(fin_type(), fin_type()) -> boolean().
+same_fin_type(Type1, Type2) ->
+ proper_types:equal_types(Type1, Type2).
+
+-spec same_rec_fun(rec_fun(), rec_fun(), arity()) -> boolean().
+same_rec_fun(RecFun1, RecFun2, NumRecArgs) ->
+ %% It's ok that we return a type, even if there's a 'true' for use of
+ %% an instance.
+ GenFun = fun(_Size) -> proper_types:exactly('$dummy') end,
+ GenFuns = lists:duplicate(NumRecArgs,GenFun),
+ same_fin_type(RecFun1(GenFuns,0), RecFun2(GenFuns,0)).
diff --git a/lib/dialyzer/test/map_SUITE_data/dialyzer_options b/lib/dialyzer/test/map_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..50991c9bc5
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, []}.
diff --git a/lib/dialyzer/test/map_SUITE_data/results/bad_argument b/lib/dialyzer/test/map_SUITE_data/results/bad_argument
new file mode 100644
index 0000000000..af61a89638
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/bad_argument
@@ -0,0 +1,5 @@
+
+bad_argument.erl:14: Function t3/0 has no local return
+bad_argument.erl:15: Guard test is_map('not_a_map') can never succeed
+bad_argument.erl:5: Function t/0 has no local return
+bad_argument.erl:6: A key of type 'b' cannot exist in a map of type #{'a':='q'}
diff --git a/lib/dialyzer/test/map_SUITE_data/results/contract b/lib/dialyzer/test/map_SUITE_data/results/contract
new file mode 100644
index 0000000000..0f6e1d0c65
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/contract
@@ -0,0 +1,7 @@
+
+contract.erl:10: Function t2/0 has no local return
+contract.erl:10: The call missing:f(#{'a':=1, 'c':=4}) breaks the contract (#{'a':=1,'b'=>2,'c'=>3}) -> 'ok'
+contract.erl:12: Function t3/0 has no local return
+contract.erl:12: The call missing:f(#{'a':=1, 'b':=2, 'e':=3}) breaks the contract (#{'a':=1,'b'=>2,'c'=>3}) -> 'ok'
+contract.erl:8: Function t1/0 has no local return
+contract.erl:8: The call missing:f(#{'b':=2}) breaks the contract (#{'a':=1,'b'=>2,'c'=>3}) -> 'ok'
diff --git a/lib/dialyzer/test/map_SUITE_data/results/contract_violation b/lib/dialyzer/test/map_SUITE_data/results/contract_violation
new file mode 100644
index 0000000000..958321618f
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/contract_violation
@@ -0,0 +1,3 @@
+
+contract_violation.erl:12: The pattern #{I:=Loc} can never match the type #{}
+contract_violation.erl:16: Invalid type specification for function contract_violation:beam_disasm_lines/2. The success typing is ('none' | <<_:32,_:_*8>>,_) -> #{pos_integer()=>{'location',_,_}}
diff --git a/lib/dialyzer/test/map_SUITE_data/results/exact b/lib/dialyzer/test/map_SUITE_data/results/exact
new file mode 100644
index 0000000000..374ada8869
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/exact
@@ -0,0 +1,3 @@
+
+exact.erl:15: Function t2/1 has no local return
+exact.erl:19: The variable _ can never match since previous clauses completely covered the type #{'a':=_, ...}
diff --git a/lib/dialyzer/test/map_SUITE_data/results/guard_update b/lib/dialyzer/test/map_SUITE_data/results/guard_update
new file mode 100644
index 0000000000..e4bc892195
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/guard_update
@@ -0,0 +1,5 @@
+
+guard_update.erl:5: Function t/0 has no local return
+guard_update.erl:6: The call guard_update:f(#{'a':=2}) will never return since it differs in the 1st argument from the success typing arguments: (#{'b':=_, ...})
+guard_update.erl:8: Clause guard cannot succeed. The variable M was matched against the type #{'a':=2}
+guard_update.erl:8: Function f/1 has no local return
diff --git a/lib/dialyzer/test/map_SUITE_data/results/initial_dataflow b/lib/dialyzer/test/map_SUITE_data/results/initial_dataflow
new file mode 100644
index 0000000000..69144f9208
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/initial_dataflow
@@ -0,0 +1,4 @@
+
+initial_dataflow.erl:11: The variable Q can never match since previous clauses completely covered the type #{}
+initial_dataflow.erl:5: Function test/0 has no local return
+initial_dataflow.erl:6: The pattern 'false' can never match the type 'true'
diff --git a/lib/dialyzer/test/map_SUITE_data/results/is_map_guard b/lib/dialyzer/test/map_SUITE_data/results/is_map_guard
new file mode 100644
index 0000000000..6ab464d865
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/is_map_guard
@@ -0,0 +1,5 @@
+
+is_map_guard.erl:13: Function t2/0 has no local return
+is_map_guard.erl:15: The call is_map_guard:explicit('still_not_map') will never return since it differs in the 1st argument from the success typing arguments: (map())
+is_map_guard.erl:6: Function t1/0 has no local return
+is_map_guard.erl:8: The call is_map_guard:implicit('not_a_map') will never return since it differs in the 1st argument from the success typing arguments: (map())
diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_galore b/lib/dialyzer/test/map_SUITE_data/results/map_galore
new file mode 100644
index 0000000000..6ea88f01f8
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/map_galore
@@ -0,0 +1,28 @@
+
+map_galore.erl:1000: A key of type 42 cannot exist in a map of type #{1:='a', 2:='b', 4:='d', 5:='e', float()=>'c' | 'v'}
+map_galore.erl:1080: A key of type 'nonexisting' cannot exist in a map of type #{#{'map':='key', 'one':='small'}:=[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}:=[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}:=[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 16=>'a6', 26=>'b6', 36=>[54 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]}
+map_galore.erl:1082: A key of type 42 cannot exist in a map of type #{#{'map':='key', 'one':='small'}:=[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}:=[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}:=[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 16=>'a6', 26=>'b6', 36=>[54 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]}
+map_galore.erl:1140: The call map_galore:map_guard_sequence_1(#{'seq':=6, 'val':=[101,...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq':=1 | 2 | 3 | 4 | 5, 'val':=[97 | 98 | 99 | 100 | 101,...], #{'map':='key', 'one':='small'}=>[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}=>[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}=>[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[any(),...], 31:=[any(),...], 32:=[any(),...], 33:=[any(),...], 34:=[any(),...], 35:=[any(),...], 37:=[any(),...], 38:=[any(),...], 39:=[any(),...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 16=>'a6', 26=>'b6', 36=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]})
+map_galore.erl:1141: The call map_galore:map_guard_sequence_2(#{'b':=5}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':='gg' | 'kk' | 'sc' | 3 | 4, 'b'=>'other' | 3 | 4 | 5, 'c'=>'sc2', #{'map':='key', 'one':='small'}=>[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}=>[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}=>[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[any(),...], 31:=[any(),...], 32:=[any(),...], 33:=[any(),...], 34:=[any(),...], 35:=[any(),...], 37:=[any(),...], 38:=[any(),...], 39:=[any(),...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 16=>'a6', 26=>'b6', 36=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]})
+map_galore.erl:1209: The call map_galore:map_guard_sequence_1(#{'seq':=6, 'val':=[101,...], #{'map':='key', 'one':='small'}:=[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}:=[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}:=[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | 3,...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 16=>'a6', 26=>'b6', 36=>[54 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq':=1 | 2 | 3 | 4 | 5, 'val':=[97 | 98 | 99 | 100 | 101,...], #{'map':='key', 'one':='small'}=>[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}=>[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}=>[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[any(),...], 31:=[any(),...], 32:=[any(),...], 33:=[any(),...], 34:=[any(),...], 35:=[any(),...], 37:=[any(),...], 38:=[any(),...], 39:=[any(),...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 16=>'a6', 26=>'b6', 36=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]})
+map_galore.erl:1210: The call map_galore:map_guard_sequence_2(#{'b':=5, #{'map':='key', 'one':='small'}:=[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}:=[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}:=[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | 3,...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 16=>'a6', 26=>'b6', 36=>[54 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':='gg' | 'kk' | 'sc' | 3 | 4, 'b'=>'other' | 3 | 4 | 5, 'c'=>'sc2', #{'map':='key', 'one':='small'}=>[32 | 49 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'second':='small'}=>[32 | 50 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], #{'map':='key', 'third':='small'}=>[32 | 51 | 97 | 101 | 107 | 108 | 109 | 112 | 115 | 121,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 27:='b7', 28:='b8', 29:='b9', 30:=[any(),...], 31:=[any(),...], 32:=[any(),...], 33:=[any(),...], 34:=[any(),...], 35:=[any(),...], 37:=[any(),...], 38:=[any(),...], 39:=[any(),...], 'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 16=>'a6', 26=>'b6', 36=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]})
+map_galore.erl:1418: Fun application with arguments (#{'s':='none', 'v':='none'}) will never return since it differs in the 1st argument from the success typing arguments: (#{'s':='l' | 't' | 'v', 'v':='none' | <<_:16>> | [<<_:16>>,...] | {<<_:16>>,<<_:16>>}})
+map_galore.erl:1491: The test #{} =:= #{'a':=1} can never evaluate to 'true'
+map_galore.erl:1492: The test #{'a':=1} =:= #{} can never evaluate to 'true'
+map_galore.erl:1495: The test #{'a':=1} =:= #{'a':=2} can never evaluate to 'true'
+map_galore.erl:1496: The test #{'a':=2} =:= #{'a':=1} can never evaluate to 'true'
+map_galore.erl:1497: The test #{'a':=2, 'b':=1} =:= #{'a':=1, 'b':=3} can never evaluate to 'true'
+map_galore.erl:1498: The test #{'a':=1, 'b':=1} =:= #{'a':=1, 'b':=3} can never evaluate to 'true'
+map_galore.erl:1762: The call maps:get({1, 1},#{{1,float()}=>[101 | 108 | 112 | 116 | 117,...]}) will never return since the success typing arguments are (any(),map())
+map_galore.erl:1763: The call maps:get('a',#{}) will never return since the success typing arguments are (any(),map())
+map_galore.erl:1765: The call maps:get('a',#{'b':=1, 'c':=2}) will never return since the success typing arguments are (any(),map())
+map_galore.erl:186: The pattern #{'x':=2} can never match the type #{'x':=3}
+map_galore.erl:187: The pattern #{'x':=3} can never match the type {'a','b','c'}
+map_galore.erl:188: The pattern #{'x':=3} can never match the type #{'y':=3}
+map_galore.erl:189: The pattern #{'x':=3} can never match the type #{'x':=[101 | 104 | 114 | 116,...]}
+map_galore.erl:2304: Cons will produce an improper list since its 2nd argument is {'b','a'}
+map_galore.erl:2304: The call maps:from_list(nonempty_improper_list({'a','b'},{'b','a'})) will never return since it differs in the 1st argument from the success typing arguments: ([{_,_}])
+map_galore.erl:2305: The call maps:from_list('a') will never return since it differs in the 1st argument from the success typing arguments: ([{_,_}])
+map_galore.erl:2306: The call maps:from_list(42) will never return since it differs in the 1st argument from the success typing arguments: ([{_,_}])
+map_galore.erl:997: A key of type 'nonexisting' cannot exist in a map of type #{}
+map_galore.erl:998: A key of type 'nonexisting' cannot exist in a map of type #{1:='a', 2:='b', 4:='d', 5:='e', float()=>'c'}
diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_in_guard b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard
new file mode 100644
index 0000000000..1015f76128
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard
@@ -0,0 +1,4 @@
+
+map_in_guard.erl:10: The call map_in_guard:assoc_update('not_a_map') will never return since it differs in the 1st argument from the success typing arguments: (#{})
+map_in_guard.erl:13: The call map_in_guard:assoc_guard_clause('not_a_map') will never return since it differs in the 1st argument from the success typing arguments: (#{})
+map_in_guard.erl:20: The call map_in_guard:exact_guard_clause(#{}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':='q'})
diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2 b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2
new file mode 100644
index 0000000000..6bc0c010d7
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2
@@ -0,0 +1,13 @@
+
+map_in_guard2.erl:10: The call map_in_guard2:assoc_guard_clause('not_a_map') will never return since it differs in the 1st argument from the success typing arguments: (map())
+map_in_guard2.erl:12: The pattern 'true' can never match the type 'false'
+map_in_guard2.erl:14: The call map_in_guard2:exact_guard_clause(#{}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':=_, ...})
+map_in_guard2.erl:17: Clause guard cannot succeed. The variable M was matched against the type 'not_a_map'
+map_in_guard2.erl:20: Function assoc_update/1 has no local return
+map_in_guard2.erl:20: Guard test is_map(M::'not_a_map') can never succeed
+map_in_guard2.erl:22: Clause guard cannot succeed. The variable M was matched against the type 'not_a_map'
+map_in_guard2.erl:22: Function assoc_guard_clause/1 has no local return
+map_in_guard2.erl:24: Clause guard cannot succeed. The variable M was matched against the type #{}
+map_in_guard2.erl:27: Clause guard cannot succeed. The variable M was matched against the type #{}
+map_in_guard2.erl:27: Function exact_guard_clause/1 has no local return
+map_in_guard2.erl:8: The call map_in_guard2:assoc_update('not_a_map') will never return since it differs in the 1st argument from the success typing arguments: (map())
diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_size b/lib/dialyzer/test/map_SUITE_data/results/map_size
new file mode 100644
index 0000000000..fc6c1f028c
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/map_size
@@ -0,0 +1,13 @@
+
+map_size.erl:11: The pattern 1 can never match the type 0
+map_size.erl:13: Function t2/0 has no local return
+map_size.erl:15: Function p/1 has no local return
+map_size.erl:15: Guard test 1 =:= 0 can never succeed
+map_size.erl:17: Function t3/0 has no local return
+map_size.erl:21: The pattern 4 can never match the type 1 | 2 | 3
+map_size.erl:23: Function t4/0 has no local return
+map_size.erl:24: The pattern 0 can never match the type 1 | 2 | 3
+map_size.erl:26: Function t5/1 has no local return
+map_size.erl:5: Function t1/0 has no local return
+map_size.erl:7: The pattern 1 can never match the type 0
+map_size.erl:9: Function e1/0 has no local return
diff --git a/lib/dialyzer/test/map_SUITE_data/results/maps_merge b/lib/dialyzer/test/map_SUITE_data/results/maps_merge
new file mode 100644
index 0000000000..0c347b4cdb
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/maps_merge
@@ -0,0 +1,11 @@
+
+maps_merge.erl:10: The pattern #{_:=_} can never match the type #{'a':=1, 3:='ok', 'q'=>none(), 7=>none(), atom() | integer()=>_}
+maps_merge.erl:12: Function t3/0 has no local return
+maps_merge.erl:14: The pattern #{7:='q'} can never match the type #{'a':=1, 3:='ok', 'q'=>none(), 7=>none(), atom() | integer()=>_}
+maps_merge.erl:16: Function t4/0 has no local return
+maps_merge.erl:18: The pattern #{7:='q'} can never match the type #{'a':=1, 3:='ok', 'q'=>none(), 7=>none(), atom() | integer()=>_}
+maps_merge.erl:20: Function t5/0 has no local return
+maps_merge.erl:21: The pattern #{'a':=2} can never match the type #{'a':=1, 'q'=>none(), 11=>_, atom()=>_}
+maps_merge.erl:5: Function t1/0 has no local return
+maps_merge.erl:6: The pattern #{'a':=1} can never match the type #{}
+maps_merge.erl:8: Function t2/0 has no local return
diff --git a/lib/dialyzer/test/map_SUITE_data/results/opaque_key b/lib/dialyzer/test/map_SUITE_data/results/opaque_key
new file mode 100644
index 0000000000..fb7080cdc5
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/opaque_key
@@ -0,0 +1,15 @@
+
+opaque_key_adt.erl:41: Invalid type specification for function opaque_key_adt:s4/0. The success typing is () -> #{1:='a'}
+opaque_key_adt.erl:44: Invalid type specification for function opaque_key_adt:s5/0. The success typing is () -> #{2:=3}
+opaque_key_adt.erl:56: Invalid type specification for function opaque_key_adt:smt1/0. The success typing is () -> #{3:='a'}
+opaque_key_adt.erl:59: Invalid type specification for function opaque_key_adt:smt2/0. The success typing is () -> #{1:='a'}
+opaque_key_use.erl:13: The test opaque_key_use:t() =:= opaque_key_use:t(integer()) can never evaluate to 'true'
+opaque_key_use.erl:24: Attempt to test for equality between a term of type opaque_key_adt:t(integer()) and a term of opaque type opaque_key_adt:t()
+opaque_key_use.erl:37: Function adt_mm1/0 has no local return
+opaque_key_use.erl:40: The attempt to match a term of type opaque_key_adt:m() against the pattern #{A:=R} breaks the opaqueness of the term
+opaque_key_use.erl:48: Function adt_mu1/0 has no local return
+opaque_key_use.erl:51: Guard test is_map(M::opaque_key_adt:m()) breaks the opaqueness of its argument
+opaque_key_use.erl:53: Function adt_mu2/0 has no local return
+opaque_key_use.erl:56: Guard test is_map(M::opaque_key_adt:m()) breaks the opaqueness of its argument
+opaque_key_use.erl:58: Function adt_mu3/0 has no local return
+opaque_key_use.erl:60: Guard test is_map(M::opaque_key_adt:m()) breaks the opaqueness of its argument
diff --git a/lib/dialyzer/test/map_SUITE_data/results/order b/lib/dialyzer/test/map_SUITE_data/results/order
new file mode 100644
index 0000000000..7be789a11a
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/order
@@ -0,0 +1,17 @@
+
+order.erl:12: Function t2/0 has no local return
+order.erl:14: Guard test is_integer(Int::'b') can never succeed
+order.erl:16: The variable _Else can never match since previous clauses completely covered the type 'b'
+order.erl:19: Function t3/0 has no local return
+order.erl:21: Guard test is_integer(Int::'b') can never succeed
+order.erl:23: The variable _Else can never match since previous clauses completely covered the type 'b'
+order.erl:30: The variable _Else can never match since previous clauses completely covered the type 'b' | 1
+order.erl:33: Function t5/0 has no local return
+order.erl:36: The variable Atom can never match since previous clauses completely covered the type 1
+order.erl:37: The variable _Else can never match since previous clauses completely covered the type 1
+order.erl:40: Function t6/0 has no local return
+order.erl:42: Guard test is_integer(Int::'b') can never succeed
+order.erl:44: The variable _Else can never match since previous clauses completely covered the type 'b'
+order.erl:5: Function t1/0 has no local return
+order.erl:7: Guard test is_integer(Int::'b') can never succeed
+order.erl:9: The variable _Else can never match since previous clauses completely covered the type 'b'
diff --git a/lib/dialyzer/test/map_SUITE_data/results/subtract_value_flip b/lib/dialyzer/test/map_SUITE_data/results/subtract_value_flip
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/subtract_value_flip
diff --git a/lib/dialyzer/test/map_SUITE_data/results/typeflow b/lib/dialyzer/test/map_SUITE_data/results/typeflow
new file mode 100644
index 0000000000..e3378a24bb
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/typeflow
@@ -0,0 +1,4 @@
+
+typeflow.erl:14: Function t2/1 has no local return
+typeflow.erl:16: The call lists:sort(integer()) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+typeflow.erl:9: The variable _ can never match since previous clauses completely covered the type #{'a':=integer(), ...}
diff --git a/lib/dialyzer/test/map_SUITE_data/results/typeflow2 b/lib/dialyzer/test/map_SUITE_data/results/typeflow2
new file mode 100644
index 0000000000..3adf638978
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/typeflow2
@@ -0,0 +1,13 @@
+
+typeflow2.erl:10: The pattern #{'a':=X} can never match the type #{'a'=>none(), _=>maybe_improper_list() | integer()}
+typeflow2.erl:11: The pattern #{'a':=X} can never match the type #{'a'=>none(), _=>maybe_improper_list() | integer()}
+typeflow2.erl:26: Function t2/1 has no local return
+typeflow2.erl:29: The call lists:sort(integer()) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+typeflow2.erl:42: The pattern #{'a':=X} can never match since previous clauses completely covered the type #{'a':=integer()}
+typeflow2.erl:43: The variable _ can never match since previous clauses completely covered the type #{'a':=integer()}
+typeflow2.erl:48: The pattern #{} can never match since previous clauses completely covered the type #{'a':=atom() | maybe_improper_list() | integer()}
+typeflow2.erl:58: The pattern #{'a':=X} can never match the type #{'a'=>none(), _=>maybe_improper_list() | integer()}
+typeflow2.erl:59: The pattern #{'a':=X} can never match the type #{'a'=>none(), _=>maybe_improper_list() | integer()}
+typeflow2.erl:60: The pattern #{'a':=X} can never match the type #{'a'=>none(), _=>maybe_improper_list() | integer()}
+typeflow2.erl:82: The pattern #{'a':=X} can never match the type #{}
+typeflow2.erl:83: The pattern #{'a':=X} can never match the type #{}
diff --git a/lib/dialyzer/test/map_SUITE_data/results/typesig b/lib/dialyzer/test/map_SUITE_data/results/typesig
new file mode 100644
index 0000000000..3049402860
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/typesig
@@ -0,0 +1,5 @@
+
+typesig.erl:5: Function t1/0 has no local return
+typesig.erl:5: The call typesig:test(#{'a':=1}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':={number()}, ...})
+typesig.erl:6: Function t2/0 has no local return
+typesig.erl:6: The call typesig:test(#{'a':={'b'}}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':={number()}, ...})
diff --git a/lib/dialyzer/test/map_SUITE_data/src/bad_argument.erl b/lib/dialyzer/test/map_SUITE_data/src/bad_argument.erl
new file mode 100644
index 0000000000..95e2b32ddc
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/bad_argument.erl
@@ -0,0 +1,19 @@
+-module(bad_argument).
+
+-export([t/0, t2/0, t3/0]).
+
+t() ->
+ _=(id1(#{a=>q}))#{b:=9}.
+
+t2() ->
+ _ = id2(4),
+ X = id2(3),
+ _ = (#{ X => q})#{3 := p},
+ X.
+
+t3() ->
+ (id3(not_a_map))#{a => b}.
+
+id1(X) -> X.
+id2(X) -> X.
+id3(X) -> X.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/bug.erl b/lib/dialyzer/test/map_SUITE_data/src/bug.erl
new file mode 100644
index 0000000000..fc32f5641a
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/bug.erl
@@ -0,0 +1,63 @@
+-module(bug).
+
+-export([t1/0, f1/1
+ ,t2/0, f2/1
+ ,t3/0, f3/1
+ ,t4/0, f4/1
+ ,t5/0, f5/1
+ ]).
+
+t1() ->
+ V = f1(#{a=>b}),
+ case V of
+ #{a := Q} -> Q; %% Must not warn here
+ _ -> ok
+ end,
+ ok.
+
+f1(M) -> %% Should get map() succ typing
+ #{} = M.
+
+t2() ->
+ V = f2([#{a=>b}]),
+ case V of
+ [#{a := P}] -> P; %% Must not warn here
+ _ -> ok
+ end,
+ ok.
+
+f2(M) -> %% Should get [map(),...] succ typing
+ [#{}] = M.
+
+t3() ->
+ V = f3([#{a=>b},a]),
+ case V of
+ [#{a := P}, _Q] -> P; %% Must not warn here
+ _ -> ok
+ end,
+ ok.
+
+f3(M) -> %% Should get [map()|a,...] succ typing
+ [#{},a] = M.
+
+t4() ->
+ V = f4({#{a=>b},{}}),
+ case V of
+ {#{a := P},{}} -> P; %% Must not warn here
+ _ -> ok
+ end,
+ ok.
+
+f4(M) -> %% Should get {map(),{}} succ typing
+ {#{},{}} = M.
+
+t5() ->
+ V = f5(#{k=>q,a=>b}),
+ case V of
+ #{k := q, a := P} -> P; %% Must not warn here
+ _ -> ok
+ end,
+ ok.
+
+f5(M) -> %% Should get #{k:=q, ...} succ typing
+ #{k:=q} = M.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/contract.erl b/lib/dialyzer/test/map_SUITE_data/src/contract.erl
new file mode 100644
index 0000000000..2b31be0d58
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/contract.erl
@@ -0,0 +1,14 @@
+-module(missing).
+
+-export([t1/0, t2/0, t3/0, t4/0]).
+
+-spec f(#{a := 1, b => 2, c => 3}) -> ok.
+f(_) -> ok.
+
+t1() -> f(#{b => 2}).
+
+t2() -> f(#{a => 1, c => 4}).
+
+t3() -> f(#{a => 1, b => 2, e => 3}).
+
+t4() -> f(#{a => 1, b => 2}).
diff --git a/lib/dialyzer/test/map_SUITE_data/src/contract_violation.erl b/lib/dialyzer/test/map_SUITE_data/src/contract_violation.erl
new file mode 100644
index 0000000000..850f2cad34
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/contract_violation.erl
@@ -0,0 +1,29 @@
+-module(contract_violation).
+
+-export([entry/1, beam_disasm_lines/2]).
+
+%%-----------------------------------------------------------------------
+
+-type lines() :: #{non_neg_integer() => {string(), non_neg_integer()}}.
+
+entry(Bin) ->
+ I = 42,
+ case beam_disasm_lines(Bin, ':-)') of
+ #{I := Loc} -> {good, Loc};
+ _ -> bad
+ end.
+
+-spec beam_disasm_lines(binary() | none, module()) -> lines().
+
+beam_disasm_lines(none, _) -> #{};
+beam_disasm_lines(<<NumLines:32, LineBin:NumLines/binary, FileBin/binary>>,
+ _Module) ->
+ Lines = binary_to_term(LineBin),
+ Files = binary_to_term(FileBin),
+ lines_collect_items(Lines, Files, #{}).
+
+lines_collect_items([], _, Acc) -> Acc;
+lines_collect_items([{FileNo, LineNo}|Rest], Files, Acc) ->
+ #{FileNo := File} = Files,
+ lines_collect_items(
+ Rest, Files, Acc#{map_size(Acc)+1 => {location, File, LineNo}}).
diff --git a/lib/dialyzer/test/map_SUITE_data/src/exact.erl b/lib/dialyzer/test/map_SUITE_data/src/exact.erl
new file mode 100644
index 0000000000..e5ad02ec54
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/exact.erl
@@ -0,0 +1,23 @@
+-module(exact).
+
+-export([t1/1, t2/1]).
+
+t1(M = #{}) ->
+ any_map(M),
+ case M of
+ #{a := _} -> error(fail);
+ _ -> ok
+ end.
+
+any_map(X) ->
+ X#{a => 1, a := 2}.
+
+t2(M = #{}) ->
+ has_a(M),
+ case M of
+ #{a := _} -> error(ok);
+ _ -> unreachable
+ end.
+
+has_a(M) ->
+ M#{a := 1, a => 2}.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/guard_update.erl b/lib/dialyzer/test/map_SUITE_data/src/guard_update.erl
new file mode 100644
index 0000000000..19d0089401
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/guard_update.erl
@@ -0,0 +1,18 @@
+-module(guard_update).
+
+-export([t/0, t2/0]).
+
+t() ->
+ f(#{a=>2}). %% Illegal
+
+f(M)
+ when M#{b := 7} =/= q
+ -> ok.
+
+t2() ->
+ f2(#{a=>2}). %% Legal!
+
+f2(M)
+ when M#{b := 7} =/= q;
+ M =/= p
+ -> ok.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/initial_dataflow.erl b/lib/dialyzer/test/map_SUITE_data/src/initial_dataflow.erl
new file mode 100644
index 0000000000..bbc0d5682a
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/initial_dataflow.erl
@@ -0,0 +1,11 @@
+-module(initial_dataflow).
+
+-export([test/0]).
+
+test() ->
+ false = assoc_guard(#{}),
+ true = assoc_guard(not_a_map),
+ ok.
+
+assoc_guard(#{}) -> true;
+assoc_guard(Q) -> false.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/is_map_guard.erl b/lib/dialyzer/test/map_SUITE_data/src/is_map_guard.erl
new file mode 100644
index 0000000000..ceb4a8763a
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/is_map_guard.erl
@@ -0,0 +1,17 @@
+-module(is_map_guard).
+
+-export([t1/0, t2/0, implicit/1, explicit/1
+ ]).
+
+t1() ->
+ _ = implicit(#{}),
+ implicit(not_a_map).
+
+implicit(M) ->
+ M#{}.
+
+t2() ->
+ explicit(#{q=>d}),
+ explicit(still_not_map).
+
+explicit(M) when is_map(M) -> ok.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/map_galore.erl b/lib/dialyzer/test/map_SUITE_data/src/map_galore.erl
new file mode 100644
index 0000000000..2611241379
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/map_galore.erl
@@ -0,0 +1,2824 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(map_galore).
+-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_build_and_match_literals_large/1,
+ t_update_literals/1, t_update_literals_large/1,
+ t_match_and_update_literals/1, t_match_and_update_literals_large/1,
+ t_update_map_expressions/1,
+ t_update_assoc/1, t_update_assoc_large/1,
+ t_update_exact/1, t_update_exact_large/1,
+ t_guard_bifs/1,
+ t_guard_sequence/1, t_guard_sequence_large/1,
+ t_guard_update/1, t_guard_update_large/1,
+ t_guard_receive/1, t_guard_receive_large/1,
+ t_guard_fun/1,
+ t_update_deep/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ t_map_equal/1,
+ t_map_compare/1,
+ t_map_size/1,
+ t_is_map/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,
+
+ %% non specific BIF related
+ t_bif_build_and_check/1,
+ t_bif_merge_and_check/1,
+
+ %% maps module not bifs
+ t_maps_fold/1,
+ t_maps_map/1,
+ t_maps_size/1,
+ t_maps_without/1,
+
+ %% misc
+ t_erts_internal_order/1,
+ t_erts_internal_hash/1,
+ t_pdict/1,
+ t_ets/1,
+ t_dets/1,
+ t_tracing/1,
+
+ %% instruction-level tests
+ t_has_map_fields/1,
+ y_regs/1
+ ]).
+
+-include_lib("stdlib/include/ms_transform.hrl").
+
+-define(CHECK(Cond,Term),
+ case (catch (Cond)) of
+ true -> true;
+ _ -> io:format("###### CHECK FAILED ######~nINPUT: ~p~n", [Term]),
+ exit(Term)
+ end).
+
+suite() -> [].
+
+all() -> [
+ t_build_and_match_literals, t_build_and_match_literals_large,
+ t_update_literals, t_update_literals_large,
+ t_match_and_update_literals, t_match_and_update_literals_large,
+ t_update_map_expressions,
+ t_update_assoc, t_update_assoc_large,
+ t_update_exact, t_update_exact_large,
+ t_guard_bifs,
+ t_guard_sequence, t_guard_sequence_large,
+ t_guard_update, t_guard_update_large,
+ t_guard_receive, t_guard_receive_large,
+ t_guard_fun, t_list_comprehension,
+ t_update_deep,
+ t_map_equal, t_map_compare,
+ 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, t_is_map,
+
+ %% non specific BIF related
+ t_bif_build_and_check,
+ t_bif_merge_and_check,
+
+ %% maps module
+ t_maps_fold, t_maps_map,
+ t_maps_size, t_maps_without,
+
+
+ %% Other functions
+ t_erts_internal_order,
+ t_erts_internal_hash,
+ t_pdict,
+ t_ets,
+ t_tracing,
+
+ %% instruction-level tests
+ t_has_map_fields,
+ y_regs
+ ].
+
+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) ->
+ #{} = #{},
+ #{1:=a} = #{1=>a},
+ #{1:=a,2:=b} = #{1=>a,2=>b},
+ #{1:=a,2:=b,3:="c"} = #{1=>a,2=>b,3=>"c"},
+ #{1:=a,2:=b,3:="c","4":="d"} = #{1=>a,2=>b,3=>"c","4"=>"d"},
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
+ #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>},
+ #{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"},
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
+ #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g},
+
+ #{[]:=a,42.0:=b,x:={x,y},[a,b]:=list} =
+ #{[]=>a,42.0=>b,x=>{x,y},[a,b]=>list},
+
+ #{<<"hi all">> := 1} = #{<<"hi",32,"all">> => 1},
+
+ #{a:=X,a:=X=3,b:=4} = #{a=>3,b=>4}, % weird but ok =)
+
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
+ #{ 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} =
+ #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+
+ %% error case
+ %V = 32,
+ %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = #{<<"hi",V,"all">> => 1})),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = #{x=>3})),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = #{x=>3})),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = {a,b,c})),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = #{y=>3})),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = #{x=>"three"})),
+ ok.
+
+t_build_and_match_literals_large(Config) when is_list(Config) ->
+ % normal non-repeating
+ M0 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" },
+
+ #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0,
+ #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0,
+ #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0,
+ #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0,
+ #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M0,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M0,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M0,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M0,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M0,
+
+ 60 = map_size(M0),
+ 60 = maps:size(M0),
+
+ % with repeating
+ M1 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 10=>na0,20=>nb0,30=>"nc0","40"=>"nd0",<<"50">>=>"ne0",{["00"]}=>"n10",
+ 11=>na1,21=>nb1,31=>"nc1","41"=>"nd1",<<"51">>=>"ne1",{["01"]}=>"n11",
+ 12=>na2,22=>nb2,32=>"nc2","42"=>"nd2",<<"52">>=>"ne2",{["02"]}=>"n12",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+
+ 13=>na3,23=>nb3,33=>"nc3","43"=>"nd3",<<"53">>=>"ne3",{["03"]}=>"n13",
+ 14=>na4,24=>nb4,34=>"nc4","44"=>"nd4",<<"54">>=>"ne4",{["04"]}=>"n14",
+
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" },
+
+ #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1,
+ #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1,
+ #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1,
+ #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1,
+ #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1,
+
+ 60 = map_size(M1),
+ 60 = maps:size(M1),
+
+ % with floats
+
+ M2 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9"},
+
+ #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2,
+ #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2,
+ #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2,
+ #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2,
+ #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2,
+
+ #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2,
+ #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2,
+ #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2,
+ #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2,
+ #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2,
+
+ #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2,
+ #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2,
+ #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2,
+ #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2,
+ #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2,
+
+ 90 = map_size(M2),
+ 90 = maps:size(M2),
+
+ % with bignums
+ M3 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ 36893488147419103232=>big1, 73786976294838206464=>big2,
+ 147573952589676412928=>big3, 18446744073709551616=>big4,
+ 4294967296=>big5, 8589934592=>big6,
+ 4294967295=>big7, 67108863=>big8
+ },
+
+ #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3,
+ #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3,
+ #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3,
+ #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3,
+ #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,
+
+ #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3,
+ #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3,
+ #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3,
+ #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3,
+ #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3,
+
+ #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
+ #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
+ #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
+ #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
+ #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,
+
+ #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
+ #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
+ #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
+ #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
+
+ 98 = map_size(M3),
+ 98 = maps:size(M3),
+
+ %% with maps
+
+ M4 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ #{ one => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+ #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M4,
+ #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M4,
+ #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M4,
+ #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M4,
+ #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M4,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M4,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M4,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M4,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M4,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M4,
+
+ #{ #{ one => small, map => key } := "small map key 1",
+ #{ second => small, map => key } := "small map key 2",
+ #{ third => small, map => key } := "small map key 3" } = M4,
+
+ #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M4,
+
+
+ #{ 15:=V1,25:=b5,35:=V2,"45":="d5",<<"55">>:=V3,{["05"]}:="15",
+ #{ one => small, map => key } := "small map key 1",
+ #{ second => small, map => key } := V4,
+ #{ third => small, map => key } := "small map key 3",
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := V5 } = M4,
+
+ a5 = V1,
+ "c5" = V2,
+ "e5" = V3,
+ "small map key 2" = V4,
+ "large map key 1" = V5,
+
+ 95 = map_size(M4),
+ 95 = maps:size(M4),
+
+ % call for value
+
+ M5 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ #{ one => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+ #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M5,
+ #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M5,
+ #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M5,
+ #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M5,
+ #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M5,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M5,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M5,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M5,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M5,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M5,
+
+ #{ #{ one => small, map => key } := "small map key 1",
+ #{ second => small, map => key } := "small map key 2",
+ #{ third => small, map => key } := "small map key 3" } = M5,
+
+ #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M5,
+
+ 95 = map_size(M5),
+ 95 = maps:size(M5),
+
+ %% remember
+
+ #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0,
+ #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0,
+ #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0,
+ #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0,
+ #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0,
+
+ #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1,
+ #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1,
+ #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1,
+ #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1,
+ #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2,
+
+ #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2,
+ #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2,
+ #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2,
+ #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2,
+ #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2,
+
+ #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
+ #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
+ #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
+ #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
+ #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,
+
+ #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3,
+ #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3,
+ #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3,
+ #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3,
+ #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3,
+
+ #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
+ #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
+ #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
+ #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
+ #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,
+
+ #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
+ #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
+ #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
+ #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
+
+ ok.
+
+
+t_map_size(Config) when is_list(Config) ->
+ 0 = map_size(#{}),
+ 1 = map_size(#{a=>1}),
+ 1 = map_size(#{a=>"wat"}),
+ 2 = map_size(#{a=>1, b=>2}),
+ 3 = map_size(#{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),
+
+ Ks = [build_key(fun(K) -> <<1,K:32,1>> end,I)||I<-lists:seq(1,100)],
+ ok = build_and_check_size(Ks,0,#{}),
+
+ %% try deep collisions
+ %% statistically we get another subtree at 50k -> 100k elements
+ %% Try to be nice and don't use too much memory in the testcase,
+
+ N = 500000,
+ Is = lists:seq(1,N),
+ N = map_size(maps:from_list([{I,I}||I<-Is])),
+ N = map_size(maps:from_list([{<<I:32>>,I}||I<-Is])),
+ N = map_size(maps:from_list([{integer_to_list(I),I}||I<-Is])),
+ N = map_size(maps:from_list([{float(I),I}||I<-Is])),
+
+ %% Error cases.
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},_}} =
+ (catch map_size(T))
+ end),
+ ok.
+
+build_and_check_size([K|Ks],N,M0) ->
+ N = map_size(M0),
+ M1 = M0#{ K => K },
+ build_and_check_size(Ks,N + 1,M1);
+build_and_check_size([],N,M) ->
+ N = map_size(M),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+t_is_map(Config) when is_list(Config) ->
+ true = is_map(#{}),
+ true = is_map(#{a=>1}),
+ false = is_map({a,b}),
+ false = is_map(x),
+ if is_map(#{}) -> ok end,
+ if is_map(#{b=>1}) -> ok end,
+ if not is_map([1,2,3]) -> ok end,
+ if not is_map(x) -> ok end,
+ ok.
+
+% test map updates without matching
+t_update_literals_large(Config) when is_list(Config) ->
+ Map = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ #{ one => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+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,
+ #{ "one" => small, map => key } => "small map key 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 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M1 = #{},
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
+ ok.
+
+t_match_and_update_literals_large(Config) when is_list(Config) ->
+ Map = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ x=>0,y=>"untouched",z=>"also untouched",q=>1,
+
+ #{ "one" => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+ #{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 = Map#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M1 = Map#{},
+ 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,
+ #{ "one" => small, map => key } := "small map key 1" } = 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 },
+ Ks = lists:seq($a,$z),
+ #{ "aa" := {$a,$a}, "ac":=41, "dc":=42 } =
+ (maps:from_list([{[K1,K2],{K1,K2}}|| K1 <- Ks, K2 <- Ks]))#{ "ac" := 41, "dc" => 42 },
+
+ %% Error cases.
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},_}} =
+ (catch (T)#{a:=42,b=>2})
+ end),
+ ok.
+
+t_update_assoc(Config) when is_list(Config) ->
+ M0 = #{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.
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},_}} =
+ (catch T#{nonexisting=>val})
+ end),
+ ok.
+
+
+t_update_assoc_large(Config) when is_list(Config) ->
+ M0 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ #{ one => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,10.0:=fa0,4:=[a,b,c],25:=b5} = M1,
+ #{ 10:=43, 24:=b4, 15:=a5, 35:="c5", 2.0:=100, 13.0:=fa3, 4.0:=[a,b,c]} =
+ M0#{1.0=>float,10:=43,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{13.0=>new},
+ #{10:=a0,20:=b0,13.0:=new,"40":="d0",<<"50">>:="e0"} = M2,
+ M2 = M0#{13.0:=wrong,13.0=>new},
+
+ ok.
+
+t_update_exact(Config) when is_list(Config) ->
+ M0 = #{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 = #{ 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.
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},_}} =
+ (catch T#{nonexisting=>val})
+ end),
+ Empty = #{},
+ {'EXIT',{{badkey,nonexisting},_}} = (catch Empty#{nonexisting:=val}),
+ {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+
+ ok.
+
+t_update_exact_large(Config) when is_list(Config) ->
+ M0 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ #{ one => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+
+ M1 = M0#{10:=42,<<"55">>:=100,10.0:=[a,b,c]},
+ #{ 10:=42,<<"55">>:=100,{["05"]}:="15",10.0:=[a,b,c],
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1" } = M1,
+
+ M1 = M0#{10:=wrong,10=>42,<<"55">>=>wrong,<<"55">>:=100,10.0:=[a,b,c]},
+
+ M2 = M0#{13.0:=new},
+ #{10:=a0,20:=b0,13.0:=new} = M2,
+ M2 = M0#{13.0=>wrong,13.0:=new},
+
+ %% Errors cases.
+ {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+
+ ok.
+
+t_update_deep(Config) when is_list(Config) ->
+ N = 250000,
+ M0 = maps:from_list([{integer_to_list(I),a}||I<-lists:seq(1,N)]),
+ #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0,
+
+ M1 = M0#{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b },
+ #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0,
+ #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1,
+
+ M2 = M0#{ "1" => c, "10" => c, "100" => c, "1000" => c, "10000" => c },
+ #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0,
+ #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1,
+ #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M2,
+
+ M3 = M2#{ "n1" => d, "n10" => d, "n100" => d, "n1000" => d, "n10000" => d },
+ #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0,
+ #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1,
+ #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M2,
+ #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M3,
+ #{ "n1" := d, "n10" := d, "n100" := d, "n1000" := d, "n10000" := d } = M3,
+ 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=>"a"}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>"b"}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>"c"}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>"d"}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>"e"}),
+
+ {1,M1} = map_guard_sequence_2(M1 = #{a=>3}),
+ {2,M2} = map_guard_sequence_2(M2 = #{a=>4, b=>4}),
+ {3,gg,M3} = map_guard_sequence_2(M3 = #{a=>gg, b=>4}),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = #{a=>sc, b=>3, c=>sc2}),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = #{a=>kk, b=>other, c=>sc2}),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>"e"})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+t_guard_sequence_large(Config) when is_list(Config) ->
+ M0 = #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19",
+
+ 10.0=>fa0,20.0=>fb0,30.0=>"fc0",
+ 11.0=>fa1,21.0=>fb1,31.0=>"fc1",
+ 12.0=>fa2,22.0=>fb2,32.0=>"fc2",
+ 13.0=>fa3,23.0=>fb3,33.0=>"fc3",
+ 14.0=>fa4,24.0=>fb4,34.0=>"fc4",
+
+ 15.0=>fa5,25.0=>fb5,35.0=>"fc5",
+ 16.0=>fa6,26.0=>fb6,36.0=>"fc6",
+ 17.0=>fa7,27.0=>fb7,37.0=>"fc7",
+ 18.0=>fa8,28.0=>fb8,38.0=>"fc8",
+ 19.0=>fa9,29.0=>fb9,39.0=>"fc9",
+
+ #{ one => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+ {1, "a"} = map_guard_sequence_1(M0#{seq=>1,val=>"a"}),
+ {2, "b"} = map_guard_sequence_1(M0#{seq=>2,val=>"b"}),
+ {3, "c"} = map_guard_sequence_1(M0#{seq=>3,val=>"c"}),
+ {4, "d"} = map_guard_sequence_1(M0#{seq=>4,val=>"d"}),
+ {5, "e"} = map_guard_sequence_1(M0#{seq=>5,val=>"e"}),
+
+ {1,M1} = map_guard_sequence_2(M1 = M0#{a=>3}),
+ {2,M2} = map_guard_sequence_2(M2 = M0#{a=>4, b=>4}),
+ {3,gg,M3} = map_guard_sequence_2(M3 = M0#{a=>gg, b=>4}),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = M0#{a=>sc, b=>3, c=>sc2}),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = M0#{a=>kk, b=>other, c=>sc2}),
+
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(M0#{seq=>6,val=>"e"})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(M0#{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.
+
+t_guard_update_large(Config) when is_list(Config) ->
+ M0 = #{ 70=>a0,80=>b0,90=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10",
+ 71=>a1,81=>b1,91=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11",
+ 72=>a2,82=>b2,92=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12",
+ 73=>a3,83=>b3,93=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13",
+ 74=>a4,84=>b4,94=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14",
+
+ 75=>a5,85=>b5,95=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15",
+ 76=>a6,86=>b6,96=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16",
+ 77=>a7,87=>b7,97=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17",
+ 78=>a8,88=>b8,98=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18",
+ 79=>a9,89=>b9,99=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19",
+
+ 70.0=>fa0,80.0=>fb0,90.0=>"fc0",
+ 71.0=>fa1,81.0=>fb1,91.0=>"fc1",
+ 72.0=>fa2,82.0=>fb2,92.0=>"fc2",
+ 73.0=>fa3,83.0=>fb3,93.0=>"fc3",
+ 74.0=>fa4,84.0=>fb4,94.0=>"fc4",
+
+ 75.0=>fa5,85.0=>fb5,95.0=>"fc5",
+ 76.0=>fa6,86.0=>fb6,96.0=>"fc6",
+ 77.0=>fa7,87.0=>fb7,97.0=>"fc7",
+ 78.0=>fa8,88.0=>fb8,98.0=>"fc8",
+ 79.0=>fa9,89.0=>fb9,99.0=>"fc9",
+
+ #{ one => small, map => key } => "small map key 1",
+ #{ second => small, map => key } => "small map key 2",
+ #{ third => small, map => key } => "small map key 3",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",
+
+ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
+ 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
+ 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
+ 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
+ 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",
+
+ 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
+ k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
+ 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
+ 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
+ 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" },
+
+
+ error = map_guard_update(M0#{},M0#{}),
+ first = map_guard_update(M0#{},M0#{x=>first}),
+ second = map_guard_update(M0#{y=>old}, M0#{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.
+
+-define(t_guard_receive_large_procs, 1500).
+
+t_guard_receive_large(Config) when is_list(Config) ->
+ M = lists:foldl(fun(_,#{procs := Ps } = M) ->
+ M#{ procs := Ps#{ spawn_link(fun() -> grecv_loop() end) => 0 }}
+ end, #{procs => #{}, done => 0}, lists:seq(1,?t_guard_receive_large_procs)),
+ lists:foreach(fun(Pid) ->
+ Pid ! {self(), hello}
+ end, maps:keys(maps:get(procs,M))),
+ ok = guard_receive_large_loop(M),
+ ok.
+
+guard_receive_large_loop(#{done := ?t_guard_receive_large_procs}) ->
+ ok;
+guard_receive_large_loop(M) ->
+ receive
+ #{pid := Pid, msg := hello} ->
+ case M of
+ #{done := Count, procs := #{Pid := 150}} ->
+ Pid ! {self(), done},
+ guard_receive_large_loop(M#{done := Count + 1});
+ #{procs := #{Pid := Count} = Ps} ->
+ Pid ! {self(), hello},
+ guard_receive_large_loop(M#{procs := Ps#{Pid := Count + 1}})
+ end
+ end.
+
+grecv_loop() ->
+ receive
+ {_, done} ->
+ ok;
+ {Pid, hello} ->
+ Pid ! #{pid=>self(), msg=>hello},
+ grecv_loop()
+ end.
+
+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]],
+
+ Ks = lists:seq($a,$z),
+ Ms = [#{[K1,K2]=>{K1,K2}} || K1 <- Ks, K2 <- Ks],
+ [#{"aa" := {$a,$a}},#{"ab":={$a,$b}}|_] = Ms,
+ [#{"zz" := {$z,$z}},#{"zy":={$z,$y}}|_] = lists:reverse(Ms),
+ 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} < #{ a => 1, b => 1, c => 1},
+ true = #{ b => 1, a => 1} < #{ c => 1, a => 1, b => 1},
+ false = #{ c => 1, b => 1, a => 1} < #{ c => 1, a => 1},
+
+ %% key order
+ true = #{ a => 1 } < #{ b => 1},
+ false = #{ b => 1 } < #{ a => 1},
+ true = #{ a => 1, b => 1, c => 1 } < #{ b => 1, c => 1, d => 1},
+ true = #{ b => 1, c => 1, d => 1 } > #{ a => 1, b => 1, c => 1},
+ true = #{ c => 1, b => 1, a => 1 } < #{ b => 1, c => 1, d => 1},
+ true = #{ "a" => 1 } < #{ <<"a">> => 1},
+ false = #{ <<"a">> => 1 } < #{ "a" => 1},
+ true = #{ 1 => 1 } < #{ 1.0 => 1},
+ false = #{ 1.0 => 1 } < #{ 1 => 1},
+
+ %% value order
+ true = #{ a => 1 } < #{ a => 2},
+ false = #{ a => 2 } < #{ a => 1},
+ false = #{ a => 2, b => 1 } < #{ a => 1, b => 3},
+ true = #{ a => 1, b => 1 } < #{ a => 1, b => 3},
+ false = #{ a => 1 } < #{ a => 1.0},
+ false = #{ a => 1.0 } < #{ a => 1},
+
+ true = #{ "a" => "hi", b => 134 } == #{ b => 134,"a" => "hi"},
+
+ %% large maps
+
+ M = maps:from_list([{I,I}||I <- lists:seq(1,500)]),
+
+ %% size order
+ true = M#{ a => 1, b => 2} < M#{ a => 1, b => 1, c => 1},
+ true = M#{ b => 1, a => 1} < M#{ c => 1, a => 1, b => 1},
+ false = M#{ c => 1, b => 1, a => 1} < M#{ c => 1, a => 1},
+
+ %% key order
+ true = M#{ a => 1 } < M#{ b => 1},
+ false = M#{ b => 1 } < M#{ a => 1},
+ true = M#{ a => 1, b => 1, c => 1 } < M#{ b => 1, c => 1, d => 1},
+ true = M#{ b => 1, c => 1, d => 1 } > M#{ a => 1, b => 1, c => 1},
+ true = M#{ c => 1, b => 1, a => 1 } < M#{ b => 1, c => 1, d => 1},
+ true = M#{ "a" => 1 } < M#{ <<"a">> => 1},
+ false = M#{ <<"a">> => 1 } < #{ "a" => 1},
+ true = M#{ 1 => 1 } < maps:remove(1,M#{ 1.0 => 1}),
+ false = M#{ 1.0 => 1 } < M#{ 1 => 1},
+
+ %% value order
+ true = M#{ a => 1 } < M#{ a => 2},
+ false = M#{ a => 2 } < M#{ a => 1},
+ false = M#{ a => 2, b => 1 } < M#{ a => 1, b => 3},
+ true = M#{ a => 1, b => 1 } < M#{ a => 1, b => 3},
+ false = M#{ a => 1 } < M#{ a => 1.0},
+ false = M#{ a => 1.0 } < M#{ a => 1},
+
+ true = M#{ "a" => "hi", b => 134 } == M#{ 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_map_equal(Config) when is_list(Config) ->
+ true = #{} =:= #{},
+ false = #{} =:= #{a=>1},
+ false = #{a=>1} =:= #{},
+ true = #{ "a" => "hi", b => 134 } =:= #{ b => 134,"a" => "hi"},
+
+ false = #{ a => 1 } =:= #{ a => 2},
+ false = #{ a => 2 } =:= #{ a => 1},
+ false = #{ a => 2, b => 1 } =:= #{ a => 1, b => 3},
+ false = #{ a => 1, b => 1 } =:= #{ a => 1, b => 3},
+
+ true = #{ a => 1 } =:= #{ a => 1},
+ true = #{ "a" => 2 } =:= #{ "a" => 2},
+ true = #{ "a" => 2, b => 3 } =:= #{ "a" => 2, b => 3},
+ true = #{ a => 1, b => 3, c => <<"wat">> } =:= #{ a => 1, b => 3, c=><<"wat">>},
+ ok.
+
+
+t_map_compare(Config) when is_list(Config) ->
+ Seed = {erlang:monotonic_time(),
+ erlang:time_offset(),
+ erlang:unique_integer()},
+ io:format("seed = ~p\n", [Seed]),
+ random:seed(Seed),
+ repeat(100, fun(_) -> float_int_compare() end, []),
+ repeat(100, fun(_) -> recursive_compare() end, []),
+ ok.
+
+float_int_compare() ->
+ Terms = numeric_keys(3),
+ %%io:format("Keys to use: ~p\n", [Terms]),
+ Pairs = lists:map(fun(K) -> list_to_tuple([{K,V} || V <- Terms]) end, Terms),
+ lists:foreach(fun(Size) ->
+ MapGen = fun() -> map_gen(list_to_tuple(Pairs), Size) end,
+ repeat(100, fun do_compare/1, [MapGen, MapGen])
+ end,
+ lists:seq(1,length(Terms))),
+ ok.
+
+numeric_keys(N) ->
+ lists:foldl(fun(_,Acc) ->
+ Int = random:uniform(N*4) - N*2,
+ Float = float(Int),
+ [Int, Float, Float * 0.99, Float * 1.01 | Acc]
+ end,
+ [],
+ lists:seq(1,N)).
+
+
+repeat(0, _, _) ->
+ ok;
+repeat(N, Fun, Arg) ->
+ Fun(Arg),
+ repeat(N-1, Fun, Arg).
+
+copy_term(T) ->
+ Papa = self(),
+ P = spawn_link(fun() -> receive Msg -> Papa ! Msg end end),
+ P ! T,
+ receive R -> R end.
+
+do_compare([Gen1, Gen2]) ->
+ M1 = Gen1(),
+ M2 = Gen2(),
+ %%io:format("Maps to compare: ~p AND ~p\n", [M1, M2]),
+ C = (M1 < M2),
+ Erlang = maps_lessthan(M1, M2),
+ C = Erlang,
+ ?CHECK(M1==M1, M1),
+
+ %% Change one key from int to float (or vice versa) and check compare
+ ML1 = maps:to_list(M1),
+ {K1,V1} = lists:nth(random:uniform(length(ML1)), ML1),
+ case K1 of
+ I when is_integer(I) ->
+ case maps:find(float(I),M1) of
+ error ->
+ M1f = maps:remove(I, maps:put(float(I), V1, M1)),
+ ?CHECK(M1f > M1, [M1f, M1]);
+ _ -> ok
+ end;
+
+ F when is_float(F), round(F) == F ->
+ case maps:find(round(F),M1) of
+ error ->
+ M1i = maps:remove(F, maps:put(round(F), V1, M1)),
+ ?CHECK(M1i < M1, [M1i, M1]);
+ _ -> ok
+ end;
+
+ _ -> ok % skip floats with decimals
+ end,
+
+ ?CHECK(M2 == M2, [M2]).
+
+
+maps_lessthan(M1, M2) ->
+ case {maps:size(M1),maps:size(M2)} of
+ {_S,_S} ->
+ {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))),
+ {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))),
+
+ case erts_internal:cmp_term(K1,K2) of
+ -1 -> true;
+ 0 -> (V1 < V2);
+ 1 -> false
+ end;
+
+ {S1, S2} ->
+ S1 < S2
+ end.
+
+term_sort(L) ->
+ lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) =< 0 end,
+ L).
+
+
+cmp(T1, T2, Exact) when is_tuple(T1) and is_tuple(T2) ->
+ case {size(T1),size(T2)} of
+ {_S,_S} -> cmp(tuple_to_list(T1), tuple_to_list(T2), Exact);
+ {S1,S2} when S1 < S2 -> -1;
+ {S1,S2} when S1 > S2 -> 1
+ end;
+
+cmp([H1|T1], [H2|T2], Exact) ->
+ case cmp(H1,H2, Exact) of
+ 0 -> cmp(T1,T2, Exact);
+ C -> C
+ end;
+
+cmp(M1, M2, Exact) when is_map(M1) andalso is_map(M2) ->
+ cmp_maps(M1,M2,Exact);
+cmp(M1, M2, Exact) ->
+ cmp_others(M1, M2, Exact).
+
+cmp_maps(M1, M2, Exact) ->
+ case {maps:size(M1),maps:size(M2)} of
+ {_S,_S} ->
+ {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))),
+ {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))),
+
+ case cmp(K1, K2, true) of
+ 0 -> cmp(V1, V2, Exact);
+ C -> C
+ end;
+
+ {S1,S2} when S1 < S2 -> -1;
+ {S1,S2} when S1 > S2 -> 1
+ end.
+
+cmp_others(I, F, true) when is_integer(I), is_float(F) ->
+ -1;
+cmp_others(F, I, true) when is_float(F), is_integer(I) ->
+ 1;
+cmp_others(T1, T2, _) ->
+ case {T1<T2, T1==T2} of
+ {true,false} -> -1;
+ {false,true} -> 0;
+ {false,false} -> 1
+ end.
+
+map_gen(Pairs, Size) ->
+ {_,L} = lists:foldl(fun(_, {Keys, Acc}) ->
+ KI = random:uniform(size(Keys)),
+ K = element(KI,Keys),
+ KV = element(random:uniform(size(K)), K),
+ {erlang:delete_element(KI,Keys), [KV | Acc]}
+ end,
+ {Pairs, []},
+ lists:seq(1,Size)),
+
+ maps:from_list(L).
+
+
+recursive_compare() ->
+ Leafs = {atom, 17, 16.9, 17.1, [], self(), spawn(fun() -> ok end), make_ref(), make_ref()},
+ {A, B} = term_gen_recursive(Leafs, 0, 0),
+ %%io:format("Recursive term A = ~p\n", [A]),
+ %%io:format("Recursive term B = ~p\n", [B]),
+
+ ?CHECK({true,false} =:= case do_cmp(A, B, false) of
+ -1 -> {A<B, A>=B};
+ 0 -> {A==B, A/=B};
+ 1 -> {A>B, A=<B}
+ end,
+ {A,B}),
+ A2 = copy_term(A),
+ ?CHECK(A == A2, {A,A2}),
+ ?CHECK(0 =:= cmp(A, A2, false), {A,A2}),
+
+ B2 = copy_term(B),
+ ?CHECK(B == B2, {B,B2}),
+ ?CHECK(0 =:= cmp(B, B2, false), {B,B2}),
+ ok.
+
+do_cmp(A, B, Exact) ->
+ C = cmp(A, B, Exact),
+ C.
+
+%% Generate two terms {A,B} that may only differ
+%% at float vs integer types.
+term_gen_recursive(Leafs, Flags, Depth) ->
+ MaxDepth = 10,
+ Rnd = case {Flags, Depth} of
+ {_, MaxDepth} -> % Only leafs
+ random:uniform(size(Leafs)) + 3;
+ {0, 0} -> % Only containers
+ random:uniform(3);
+ {0,_} -> % Anything
+ random:uniform(size(Leafs)+3)
+ end,
+ case Rnd of
+ 1 -> % Make map
+ Size = random:uniform(size(Leafs)),
+ lists:foldl(fun(_, {Acc1,Acc2}) ->
+ {K1,K2} = term_gen_recursive(Leafs, Flags,
+ Depth+1),
+ {V1,V2} = term_gen_recursive(Leafs, Flags, Depth+1),
+ {maps:put(K1,V1, Acc1), maps:put(K2,V2, Acc2)}
+ end,
+ {maps:new(), maps:new()},
+ lists:seq(1,Size));
+ 2 -> % Make cons
+ {Car1,Car2} = term_gen_recursive(Leafs, Flags, Depth+1),
+ {Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags, Depth+1),
+ {[Car1 | Cdr1], [Car2 | Cdr2]};
+ 3 -> % Make tuple
+ Size = random:uniform(size(Leafs)),
+ L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags, Depth+1) end,
+ lists:seq(1,Size)),
+ {L1, L2} = lists:unzip(L),
+ {list_to_tuple(L1), list_to_tuple(L2)};
+
+ N -> % Make leaf
+ case element(N-3, Leafs) of
+ I when is_integer(I) ->
+ case random:uniform(4) of
+ 1 -> {I, float(I)};
+ 2 -> {float(I), I};
+ _ -> {I,I}
+ end;
+ T -> {T,T}
+ end
+ end.
+
+%% BIFs
+t_bif_map_get(Config) when is_list(Config) ->
+ %% small map
+ 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"}),
+
+ M0 = #{ k1=>"v1", <<"k2">> => <<"v3">> },
+ "v4" = maps:get(<<"k2">>, M0#{<<"k2">> => "v4"}),
+
+ %% large map
+ M1 = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++
+ [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"},
+ {k1,"v1"},{<<"k2">>,"v3"}]),
+ 1 = maps:get(a, M1),
+ 2 = maps:get(b, M1),
+ "hi" = maps:get("hello", M1),
+ "tuple hi" = maps:get({1,1.0}, M1),
+ "v3" = maps:get(<<"k2">>, M1),
+
+ %% error cases
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,get,_,_}|_]}} =
+ (catch maps:get(a, T))
+ end),
+
+ {'EXIT',{{badkey,{1,1}},[{maps,get,_,_}|_]}} =
+ (catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
+ {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{})),
+ {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} =
+ (catch maps:get(a, #{b=>1, c=>2})),
+ ok.
+
+t_bif_map_find(Config) when is_list(Config) ->
+ %% small map
+ {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"}),
+
+ M0 = #{ k1=>"v1", <<"k2">> => <<"v3">> },
+ {ok, "v4"} = maps:find(<<"k2">>, M0#{ <<"k2">> => "v4" }),
+
+ %% large map
+ M1 = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++
+ [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"},
+ {k1,"v1"},{<<"k2">>,"v3"}]),
+ {ok, 1} = maps:find(a, M1),
+ {ok, 2} = maps:find(b, M1),
+ {ok, "hi"} = maps:find("hello", M1),
+ {ok, "tuple hi"} = maps:find({1,1.0}, M1),
+ {ok, "v3"} = maps:find(<<"k2">>, M1),
+
+ %% 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
+
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,find,_,_}|_]}} =
+ (catch maps:find(a, T))
+ end),
+ 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, #{}),
+
+ 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
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,is_key,_,_}|_]}} =
+ (catch maps:is_key(a, T))
+ end),
+ ok.
+
+t_bif_map_keys(Config) when is_list(Config) ->
+ [] = maps:keys(#{}),
+
+ [1,2,3,4,5] = lists:sort(maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})),
+ [1,2,3,4,5] = lists:sort(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">>] = lists:sort(maps:keys(M1)),
+
+ %% error case
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,keys,_,_}|_]}} =
+ (catch maps:keys(T))
+ end),
+ 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),
+
+ %% try deep collisions
+ N = 150000,
+ Is = lists:seq(1,N),
+ M2 = maps:from_list([{I,I}||I<-Is]),
+ 150000 = maps:size(M2),
+ M3 = maps:from_list([{<<I:32>>,I}||I<-Is]),
+ 150000 = maps:size(M3),
+ M4 = maps:merge(M2,M3),
+ 300000 = maps:size(M4),
+ M5 = maps:from_list([{integer_to_list(I),I}||I<-Is]),
+ 150000 = maps:size(M5),
+ M6 = maps:merge(M4,M5),
+ 450000 = maps:size(M6),
+ M7 = maps:from_list([{float(I),I}||I<-Is]),
+ 150000 = maps:size(M7),
+ M8 = maps:merge(M7,M6),
+ 600000 = maps:size(M8),
+
+ #{ 1 := 1, "1" := 1, <<1:32>> := 1 } = M8,
+ #{ 10 := 10, "10" := 10, <<10:32>> := 10 } = M8,
+ #{ 100 := 100, "100" := 100, <<100:32>> := 100 } = M8,
+ #{ 1000 := 1000, "1000" := 1000, <<1000:32>> := 1000 } = M8,
+ #{ 10000 := 10000, "10000" := 10000, <<10000:32>> := 10000 } = M8,
+ #{ 100000 := 100000, "100000" := 100000, <<100000:32>> := 100000 } = M8,
+
+ %% overlapping
+ M8 = maps:merge(M2,M8),
+ M8 = maps:merge(M3,M8),
+ M8 = maps:merge(M4,M8),
+ M8 = maps:merge(M5,M8),
+ M8 = maps:merge(M6,M8),
+ M8 = maps:merge(M7,M8),
+ M8 = maps:merge(M8,M8),
+
+ %% maps:merge/2 and mixed
+
+ Ks1 = [764492191,2361333849], %% deep collision
+ Ks2 = lists:seq(1,33),
+ M9 = maps:from_list([{K,K}||K <- Ks1]),
+ M10 = maps:from_list([{K,K}||K <- Ks2]),
+ M11 = maps:merge(M9,M10),
+ ok = check_keys_exist(Ks1 ++ Ks2, M11),
+
+ %% error case
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} =
+ (catch maps:merge(#{}, T)),
+ {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} =
+ (catch maps:merge(T, #{})),
+ {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} =
+ (catch maps:merge(T, T))
+ end),
+ 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", #{}),
+
+ true = is_members(["hi"],maps:keys(M1)),
+ true = is_members(["hello"],maps:values(M1)),
+
+ M2 = #{ int := 3 } = maps:put(int, 3, M1),
+
+ true = is_members([int,"hi"],maps:keys(M2)),
+ true = is_members([3,"hello"],maps:values(M2)),
+
+ M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2),
+
+ true = is_members([int,"hi",<<"key">>],maps:keys(M3)),
+ true = is_members([3,"hello",<<"value">>],maps:values(M3)),
+
+ M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3),
+
+ true = is_members([18446744073709551629,int,"hi",<<"key">>],maps:keys(M4)),
+ true = is_members([wat,3,"hello",<<"value">>],maps:values(M4)),
+
+ M0 = #{ 4 := number } = M5 = maps:put(4, number, M4),
+
+ true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M5)),
+ true = is_members([number,wat,3,"hello",<<"value">>],maps:values(M5)),
+
+ M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5),
+
+ true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M6)),
+ true = is_members([number,wat,3,"hello",<<"other value">>],maps:values(M6)),
+
+ %% error case
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,put,_,_}|_]}} =
+ (catch maps:put(1, a, T))
+ end),
+ ok.
+
+is_members(Ks,Ls) when length(Ks) =/= length(Ls) -> false;
+is_members(Ks,Ls) -> is_members_do(Ks,Ls).
+
+is_members_do([],[]) -> true;
+is_members_do([],_) -> false;
+is_members_do([K|Ks],Ls) ->
+ is_members_do(Ks, lists:delete(K,Ls)).
+
+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),
+ true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)),
+ true = is_members([number,wat,3,<<"value">>],maps:values(M1)),
+
+ M2 = maps:remove(int, M1),
+ true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)),
+ true = is_members([number,wat,<<"value">>],maps:values(M2)),
+
+ M3 = maps:remove(<<"key">>, M2),
+ true = is_members([4,18446744073709551629],maps:keys(M3)),
+ true = is_members([number,wat],maps:values(M3)),
+
+ M4 = maps:remove(18446744073709551629, M3),
+ true = is_members([4],maps:keys(M4)),
+ true = is_members([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
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} =
+ (catch maps:remove(a, T))
+ end),
+ 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
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,update,_,_}|_]}} =
+ (catch maps:update(1, none, T))
+ end),
+ ok.
+
+
+
+t_bif_map_values(Config) when is_list(Config) ->
+
+ [] = maps:values(#{}),
+ [1] = maps:values(#{a=>1}),
+
+ true = is_members([a,b,c,d,e],maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})),
+ true = is_members([a,b,c,d,e],maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c})),
+
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
+ true = is_members([number,3,"hello2",<<"value2">>],maps:values(M2)),
+ true = is_members([number,3,"hello",<<"value">>],maps:values(M1)),
+
+ Vs = lists:seq(1000,20000),
+ M3 = maps:from_list([{K,K}||K<-Vs]),
+ M4 = maps:merge(M1,M3),
+ M5 = maps:merge(M2,M3),
+ true = is_members(Vs,maps:values(M3)),
+ true = is_members([number,3,"hello",<<"value">>]++Vs,maps:values(M4)),
+ true = is_members([number,3,"hello2",<<"value2">>]++Vs,maps:values(M5)),
+
+ %% error case
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,values,_,_}|_]}} =
+ (catch maps:values(T))
+ end),
+ 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(#{}),
+ 33667975 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), % 78942764
+ 95332690 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), % 37338230
+ 108954384 = erlang:phash2(#{ 1 => a }), % 14363616
+ 59617982 = erlang:phash2(#{ a => 1 }), % 51612236
+
+ 42770201 = erlang:phash2(#{{} => <<>>}), % 37468437
+ 71687700 = erlang:phash2(#{<<>> => {}}), % 44049159
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 70249457 = erlang:phash2(M0), % 118679416
+ 59617982 = erlang:phash2(M1), % 51612236
+ 70249457 = erlang:phash2(M2), % 118679416
+ ok.
+
+t_bif_erlang_phash() ->
+ Sz = 1 bsl 32,
+ 1113425985 = erlang:phash(#{},Sz), % 268440612
+ 1510068139 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), % 1196461908
+ 3182345590 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), % 3944426064
+ 2927531828 = erlang:phash(#{ 1 => a },Sz), % 1394238263
+ 1670235874 = erlang:phash(#{ a => 1 },Sz), % 4066388227
+
+ 3935089469 = erlang:phash(#{{} => <<>>},Sz), % 1578050717
+ 71692856 = erlang:phash(#{<<>> => {}},Sz), % 1578050717
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 2620391445 = erlang:phash(M0,Sz), % 3590546636
+ 1670235874 = erlang:phash(M1,Sz), % 4066388227
+ 2620391445 = erlang:phash(M2,Sz), % 3590546636
+ ok.
+
+t_bif_erlang_hash() ->
+ Sz = 1 bsl 27 - 1,
+ 39684169 = erlang:hash(#{},Sz), % 5158
+ 33673142 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), % 71555838
+ 95337869 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), % 5497225
+ 108959561 = erlang:hash(#{ 1 => a },Sz), % 126071654
+ 59623150 = erlang:hash(#{ a => 1 },Sz), % 126426236
+
+ 42775386 = erlang:hash(#{{} => <<>>},Sz), % 101655720
+ 71692856 = erlang:hash(#{<<>> => {}},Sz), % 101655720
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 70254632 = erlang:hash(M0,Sz), % 38260486
+ 59623150 = erlang:hash(M1,Sz), % 126426236
+ 70254632 = erlang:hash(M2,Sz), % 38260486
+ 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()
+ >>),
+
+ %% Maps of different sizes
+ lists:foldl(fun(Key, M0) ->
+ M1 = M0#{Key => Key},
+ case Key rem 17 of
+ 0 ->
+ M1 = binary_to_term(term_to_binary(M1));
+ _ ->
+ ok
+ end,
+ M1
+ end,
+ #{},
+ lists:seq(1,10000)),
+
+ %% many maps in same binary
+ MapList = lists:foldl(fun(K, [M|_]=Acc) -> [M#{K => K} | Acc] end,
+ [#{}],
+ lists:seq(1,100)),
+ MapList = binary_to_term(term_to_binary(MapList)),
+ MapListR = lists:reverse(MapList),
+ MapListR = binary_to_term(term_to_binary(MapListR)),
+
+ %% 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,
+ 97,1,
+ 107,0,2,104,105,
+ 107,0,5,118,97,108,117,101,
+ 100,0,1,97,
+ 97,2>>)),
+
+ %% bad size (too large)
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,97,1,100,0,1,98,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 = [{erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs],
+ ok = match_encoded_map(B0, length(Ls), Ls),
+ %% 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_stripped_size(Encoded,Items,Items);
+match_encoded_map(_,_,_) -> no_match_size.
+
+match_encoded_map_stripped_size(<<>>,_,_) -> ok;
+match_encoded_map_stripped_size(B0,[{<<131,K/binary>>,<<131,V/binary>>}|Items],Ls) ->
+ Ksz = byte_size(K),
+ Vsz = byte_size(V),
+ case B0 of
+ <<K:Ksz/binary,V:Vsz/binary,B1/binary>> ->
+ match_encoded_map_stripped_size(B1,Ls,Ls);
+ _ ->
+ match_encoded_map_stripped_size(B0,Items,Ls)
+ end;
+match_encoded_map_stripped_size(_,[],_) -> fail.
+
+
+t_bif_map_to_list(Config) when is_list(Config) ->
+ [] = maps:to_list(#{}),
+ [{a,1},{b,2}] = lists:sort(maps:to_list(#{a=>1,b=>2})),
+ [{a,1},{b,2},{c,3}] = lists:sort(maps:to_list(#{c=>3,a=>1,b=>2})),
+ [{a,1},{b,2},{g,3}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2})),
+ [{a,1},{b,2},{g,3},{"c",4}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4})),
+ [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] =
+ lists:sort(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}] =
+ lists:sort(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
+ do_badmap(fun(T) ->
+ {'EXIT', {{badmap,T},_}} =
+ (catch maps:to_list(T))
+ end),
+ 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}]),
+
+ %% repeated keys (large -> small)
+ Ps1 = [{a,I}|| I <- lists:seq(1,32)],
+ Ps2 = [{a,I}|| I <- lists:seq(33,64)],
+
+ M = maps:from_list(Ps1 ++ [{b,1},{c,1}] ++ Ps2),
+ #{ a := 64, b := 1, c := 1 } = M,
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch maps:from_list([{a,b},b])),
+ {'EXIT', {badarg,_}} = (catch maps:from_list([{a,b},{b,b,3}])),
+ {'EXIT', {badarg,_}} = (catch maps:from_list([{a,b},<<>>])),
+ {'EXIT', {badarg,_}} = (catch maps:from_list([{a,b}|{b,a}])),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(a)),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(42)),
+ ok.
+
+t_bif_build_and_check(Config) when is_list(Config) ->
+ ok = check_build_and_remove(750,[
+ fun(K) -> [K,K] end,
+ fun(K) -> [float(K),K] end,
+ fun(K) -> K end,
+ fun(K) -> {1,K} end,
+ fun(K) -> {K} end,
+ fun(K) -> [K|K] end,
+ fun(K) -> [K,1,2,3,4] end,
+ fun(K) -> {K,atom} end,
+ fun(K) -> float(K) end,
+ fun(K) -> integer_to_list(K) end,
+ fun(K) -> list_to_atom(integer_to_list(K)) end,
+ fun(K) -> [K,{K,[K,{K,[K]}]}] end,
+ fun(K) -> <<K:32>> end
+ ]),
+
+ ok.
+
+check_build_and_remove(_,[]) -> ok;
+check_build_and_remove(N,[F|Fs]) ->
+ {M,Ks} = build_and_check(N, maps:new(), F, []),
+ ok = remove_and_check(Ks,M),
+ check_build_and_remove(N,Fs).
+
+build_and_check(0, M0, _, Ks) -> {M0, Ks};
+build_and_check(N, M0, F, Ks) ->
+ K = build_key(F,N),
+ M1 = maps:put(K,K,M0),
+ ok = check_keys_exist([I||{I,_} <- [{K,M1}|Ks]], M1),
+ M2 = maps:update(K,v,M1),
+ v = maps:get(K,M2),
+ build_and_check(N-1,M1,F,[{K,M1}|Ks]).
+
+remove_and_check([],_) -> ok;
+remove_and_check([{K,Mc}|Ks], M0) ->
+ K = maps:get(K,M0),
+ true = maps:is_key(K,M0),
+ true = Mc =:= M0,
+ true = M0 == Mc,
+ M1 = maps:remove(K,M0),
+ false = M1 =:= Mc,
+ false = Mc == M1,
+ false = maps:is_key(K,M1),
+ true = maps:is_key(K,M0),
+ ok = check_keys_exist([I||{I,_} <- Ks],M1),
+ error = maps:find(K,M1),
+ remove_and_check(Ks, M1).
+
+build_key(F,N) when N rem 3 =:= 0 -> F(N);
+build_key(F,N) when N rem 3 =:= 1 -> K = F(N), {K,K};
+build_key(F,N) when N rem 3 =:= 2 -> K = F(N), [K,K].
+
+check_keys_exist([], _) -> ok;
+check_keys_exist([K|Ks],M) ->
+ true = maps:is_key(K,M),
+ check_keys_exist(Ks,M).
+
+t_bif_merge_and_check(Config) when is_list(Config) ->
+
+ io:format("rand:export_seed() -> ~p\n",[rand:export_seed()]),
+
+ %% simple disjunct ones
+ %% make sure all keys are unique
+ Kss = [[a,b,c,d],
+ [1,2,3,4],
+ [],
+ ["hi"],
+ [e],
+ [build_key(fun(K) -> {small,K} end, I) || I <- lists:seq(1,32)],
+ lists:seq(5, 28),
+ lists:seq(29, 59),
+ [build_key(fun(K) -> integer_to_list(K) end, I) || I <- lists:seq(2000,10000)],
+ [build_key(fun(K) -> <<K:32>> end, I) || I <- lists:seq(1,80)],
+ [build_key(fun(K) -> {<<K:32>>} end, I) || I <- lists:seq(100,1000)]],
+
+
+ KsMs = build_keys_map_pairs(Kss),
+ Cs = [{CKs1,CM1,CKs2,CM2} || {CKs1,CM1} <- KsMs, {CKs2,CM2} <- KsMs],
+ ok = merge_and_check_combo(Cs),
+
+ %% overlapping ones
+
+ KVs1 = [{a,1},{b,2},{c,3}],
+ KVs2 = [{b,3},{c,4},{d,5}],
+ KVs = [{I,I} || I <- lists:seq(1,32)],
+ KVs3 = KVs1 ++ KVs,
+ KVs4 = KVs2 ++ KVs,
+
+ M1 = maps:from_list(KVs1),
+ M2 = maps:from_list(KVs2),
+ M3 = maps:from_list(KVs3),
+ M4 = maps:from_list(KVs4),
+
+ M12 = maps:merge(M1,M2),
+ ok = check_key_values(KVs2 ++ [{a,1}], M12),
+ M21 = maps:merge(M2,M1),
+ ok = check_key_values(KVs1 ++ [{d,5}], M21),
+
+ M34 = maps:merge(M3,M4),
+ ok = check_key_values(KVs4 ++ [{a,1}], M34),
+ M43 = maps:merge(M4,M3),
+ ok = check_key_values(KVs3 ++ [{d,5}], M43),
+
+ M14 = maps:merge(M1,M4),
+ ok = check_key_values(KVs4 ++ [{a,1}], M14),
+ M41 = maps:merge(M4,M1),
+ ok = check_key_values(KVs1 ++ [{d,5}] ++ KVs, M41),
+
+ [begin Ma = random_map(SzA, a),
+ Mb = random_map(SzB, b),
+ ok = merge_maps(Ma, Mb)
+ end || SzA <- [3,10,20,100,200,1000], SzB <- [3,10,20,100,200,1000]],
+
+ ok.
+
+% Generate random map with an average of Sz number of pairs: K -> {V,K}
+random_map(Sz, V) ->
+ random_map_insert(#{}, 0, V, Sz*2).
+
+random_map_insert(M0, K0, _, Sz) when K0 > Sz ->
+ M0;
+random_map_insert(M0, K0, V, Sz) ->
+ Key = K0 + rand:uniform(3),
+ random_map_insert(M0#{Key => {V,Key}}, Key, V, Sz).
+
+
+merge_maps(A, B) ->
+ AB = maps:merge(A, B),
+ %%io:format("A=~p\nB=~p\n",[A,B]),
+ maps_foreach(fun(K,VB) -> VB = maps:get(K, AB)
+ end, B),
+ maps_foreach(fun(K,VA) ->
+ case {maps:get(K, AB),maps:find(K, B)} of
+ {VA, error} -> ok;
+ {VB, {ok, VB}} -> ok
+ end
+ end, A),
+
+ maps_foreach(fun(K,V) ->
+ case {maps:find(K, A),maps:find(K, B)} of
+ {{ok, V}, error} -> ok;
+ {error, {ok, V}} -> ok;
+ {{ok,_}, {ok, V}} -> ok
+ end
+ end, AB),
+ ok.
+
+maps_foreach(Fun, Map) ->
+ maps:fold(fun(K,V,_) -> Fun(K,V) end, void, Map).
+
+
+check_key_values([],_) -> ok;
+check_key_values([{K,V}|KVs],M) ->
+ V = maps:get(K,M),
+ check_key_values(KVs,M).
+
+merge_and_check_combo([]) -> ok;
+merge_and_check_combo([{Ks1,M1,Ks2,M2}|Cs]) ->
+ M12 = maps:merge(M1,M2),
+ ok = check_keys_exist(Ks1 ++ Ks2, M12),
+ M21 = maps:merge(M2,M1),
+ ok = check_keys_exist(Ks1 ++ Ks2, M21),
+
+ true = M12 =:= M21,
+ M12 = M21,
+
+ merge_and_check_combo(Cs).
+
+build_keys_map_pairs([]) -> [];
+build_keys_map_pairs([Ks|Kss]) ->
+ M = maps:from_list(keys_to_pairs(Ks)),
+ ok = check_keys_exist(Ks, M),
+ [{Ks,M}|build_keys_map_pairs(Kss)].
+
+keys_to_pairs(Ks) -> [{K,K} || K <- Ks].
+
+
+%% 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.
+
+t_erts_internal_order(_Config) when is_list(_Config) ->
+ M = #{0 => 0,2147483648 => 0},
+ true = M =:= binary_to_term(term_to_binary(M)),
+
+ F1 = fun(_, _) -> 0 end,
+ F2 = fun(_, _) -> 1 end,
+ M0 = maps:from_list( [{-2147483649, 0}, {0,0}, {97, 0}, {false, 0}, {flower, 0}, {F1, 0}, {F2, 0}, {<<>>, 0}]),
+ M1 = maps:merge(M0, #{0 => 1}),
+ 8 = maps:size(M1),
+ 1 = maps:get(0,M1),
+ ok.
+
+t_erts_internal_hash(_Config) when is_list(_Config) ->
+ K1 = 0.0,
+ K2 = 0.0/-1,
+ M = maps:from_list([{I,I}||I<-lists:seq(1,32)]),
+
+ M1 = M#{ K1 => a, K2 => b },
+ b = maps:get(K2,M1),
+
+ M2 = M#{ K2 => a, K1 => b },
+ b = maps:get(K1,M2),
+
+ %% test previously faulty hash list optimization
+
+ M3 = M#{[0] => a, [0,0] => b, [0,0,0] => c, [0,0,0,0] => d},
+ a = maps:get([0],M3),
+ b = maps:get([0,0],M3),
+ c = maps:get([0,0,0],M3),
+ d = maps:get([0,0,0,0],M3),
+
+ M4 = M#{{[0]} => a, {[0,0]} => b, {[0,0,0]} => c, {[0,0,0,0]} => d},
+ a = maps:get({[0]},M4),
+ b = maps:get({[0,0]},M4),
+ c = maps:get({[0,0,0]},M4),
+ d = maps:get({[0,0,0,0]},M4),
+
+ M5 = M3#{[0,0,0] => e, [0,0,0,0] => f, [0,0,0,0,0] => g,
+ [0,0,0,0,0,0] => h, [0,0,0,0,0,0,0] => i,
+ [0,0,0,0,0,0,0,0] => j, [0,0,0,0,0,0,0,0,0] => k},
+
+ a = maps:get([0],M5),
+ b = maps:get([0,0],M5),
+ e = maps:get([0,0,0],M5),
+ f = maps:get([0,0,0,0],M5),
+ g = maps:get([0,0,0,0,0],M5),
+ h = maps:get([0,0,0,0,0,0],M5),
+ i = maps:get([0,0,0,0,0,0,0],M5),
+ j = maps:get([0,0,0,0,0,0,0,0],M5),
+ k = maps:get([0,0,0,0,0,0,0,0,0],M5),
+
+ M6 = M4#{{[0,0,0]} => e, {[0,0,0,0]} => f, {[0,0,0,0,0]} => g,
+ {[0,0,0,0,0,0]} => h, {[0,0,0,0,0,0,0]} => i,
+ {[0,0,0,0,0,0,0,0]} => j, {[0,0,0,0,0,0,0,0,0]} => k},
+
+ a = maps:get({[0]},M6),
+ b = maps:get({[0,0]},M6),
+ e = maps:get({[0,0,0]},M6),
+ f = maps:get({[0,0,0,0]},M6),
+ g = maps:get({[0,0,0,0,0]},M6),
+ h = maps:get({[0,0,0,0,0,0]},M6),
+ i = maps:get({[0,0,0,0,0,0,0]},M6),
+ j = maps:get({[0,0,0,0,0,0,0,0]},M6),
+ k = maps:get({[0,0,0,0,0,0,0,0,0]},M6),
+
+ M7 = maps:merge(M5,M6),
+
+ a = maps:get([0],M7),
+ b = maps:get([0,0],M7),
+ e = maps:get([0,0,0],M7),
+ f = maps:get([0,0,0,0],M7),
+ g = maps:get([0,0,0,0,0],M7),
+ h = maps:get([0,0,0,0,0,0],M7),
+ i = maps:get([0,0,0,0,0,0,0],M7),
+ j = maps:get([0,0,0,0,0,0,0,0],M7),
+ k = maps:get([0,0,0,0,0,0,0,0,0],M7),
+ a = maps:get({[0]},M7),
+ b = maps:get({[0,0]},M7),
+ e = maps:get({[0,0,0]},M7),
+ f = maps:get({[0,0,0,0]},M7),
+ g = maps:get({[0,0,0,0,0]},M7),
+ h = maps:get({[0,0,0,0,0,0]},M7),
+ i = maps:get({[0,0,0,0,0,0,0]},M7),
+ j = maps:get({[0,0,0,0,0,0,0,0]},M7),
+ k = maps:get({[0,0,0,0,0,0,0,0,0]},M7),
+ ok.
+
+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.
+
+t_tracing(_Config) ->
+
+ dbg:stop_clear(),
+ {ok,Tracer} = dbg:tracer(process,{fun trace_collector/2, self()}),
+ dbg:p(self(),c),
+
+ %% Test basic map call
+ {ok,_} = dbg:tpl(?MODULE,id,x),
+ #{ a => b },
+ {trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
+ {trace,_,return_from,{?MODULE,id,1},#{ a := b }} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test equals in argument list
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==','$1',#{ b => c}}],
+ [{return_trace}]}]),
+ #{ a => b },
+ #{ b => c },
+ {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
+ {trace,_,return_from,{?MODULE,id,1},#{ b := c }} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test match in head
+ {ok,_} = dbg:tpl(?MODULE,id,[{[#{b => c}],[],[]}]),
+ #{ a => b },
+ #{ b => c },
+ {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ % Test map guard bifs
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{is_map,{element,1,'$1'}}],[]}]),
+ #{ a => b },
+ {1,2},
+ {#{ a => b},2},
+ {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==',{map_size,{element,1,'$1'}},2}],[]}]),
+ #{ a => b },
+ {1,2},
+ {#{ a => b},2},
+ {#{ a => b, b => c},atom},
+ {trace,_,call,{?MODULE,id,[{#{ a := b, b := c },atom}]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end),
+ %dbg:tpl(?MODULE,id,MS),
+ %#{ a => b },
+ %#{ b => c },
+ %{trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
+ %dbg:ctpl(),
+
+ %% Check to extra messages
+ timeout = getmsg(Tracer),
+
+ dbg:stop_clear(),
+ ok.
+
+getmsg(_Tracer) ->
+ receive V -> V after 100 -> timeout end.
+
+trace_collector(Msg,Parent) ->
+ io:format("~p~n",[Msg]),
+ Parent ! Msg,
+ Parent.
+
+t_has_map_fields(Config) when is_list(Config) ->
+ true = has_map_fields_1(#{one=>1}),
+ true = has_map_fields_1(#{one=>1,two=>2}),
+ false = has_map_fields_1(#{two=>2}),
+ false = has_map_fields_1(#{}),
+
+ true = has_map_fields_2(#{c=>1,b=>2,a=>3}),
+ true = has_map_fields_2(#{c=>1,b=>2,a=>3,x=>42}),
+ false = has_map_fields_2(#{b=>2,c=>1}),
+ false = has_map_fields_2(#{x=>y}),
+ false = has_map_fields_2(#{}),
+
+ true = has_map_fields_3(#{c=>1,b=>2,a=>3}),
+ true = has_map_fields_3(#{c=>1,b=>2,a=>3,[]=>42}),
+ true = has_map_fields_3(#{b=>2,a=>3,[]=>42,42.0=>43}),
+ true = has_map_fields_3(#{a=>3,[]=>42,42.0=>43}),
+ true = has_map_fields_3(#{[]=>42,42.0=>43}),
+ false = has_map_fields_3(#{b=>2,c=>1}),
+ false = has_map_fields_3(#{[]=>y}),
+ false = has_map_fields_3(#{42.0=>x,a=>99}),
+ false = has_map_fields_3(#{}),
+
+ ok.
+
+has_map_fields_1(#{one:=_}) -> true;
+has_map_fields_1(#{}) -> false.
+
+has_map_fields_2(#{a:=_,b:=_,c:=_}) -> true;
+has_map_fields_2(#{}) -> false.
+
+has_map_fields_3(#{a:=_,b:=_}) -> true;
+has_map_fields_3(#{[]:=_,42.0:=_}) -> true;
+has_map_fields_3(#{}) -> false.
+
+y_regs(Config) when is_list(Config) ->
+ Val = [length(Config)],
+ Map0 = y_regs_update(#{}, Val),
+ Map2 = y_regs_update(Map0, Val),
+
+ Map3 = maps:from_list([{I,I*I} || I <- lists:seq(1, 100)]),
+ Map4 = y_regs_update(Map3, Val),
+
+ true = is_map(Map2) andalso is_map(Map4),
+
+ ok.
+
+y_regs_update(Map0, Val0) ->
+ Val1 = {t,Val0},
+ K1 = {key,1},
+ K2 = {key,2},
+ Map1 = Map0#{K1=>K1,
+ a=>Val0,b=>Val0,c=>Val0,d=>Val0,e=>Val0,
+ f=>Val0,g=>Val0,h=>Val0,i=>Val0,j=>Val0,
+ k=>Val0,l=>Val0,m=>Val0,n=>Val0,o=>Val0,
+ p=>Val0,q=>Val0,r=>Val0,s=>Val0,t=>Val0,
+ u=>Val0,v=>Val0,w=>Val0,x=>Val0,y=>Val0,
+ z=>Val0,
+ aa=>Val0,ab=>Val0,ac=>Val0,ad=>Val0,ae=>Val0,
+ af=>Val0,ag=>Val0,ah=>Val0,ai=>Val0,aj=>Val0,
+ ak=>Val0,al=>Val0,am=>Val0,an=>Val0,ao=>Val0,
+ ap=>Val0,aq=>Val0,ar=>Val0,as=>Val0,at=>Val0,
+ au=>Val0,av=>Val0,aw=>Val0,ax=>Val0,ay=>Val0,
+ az=>Val0,
+ K2=>[a,b,c]},
+ Map2 = Map1#{K1=>K1,
+ a:=Val1,b:=Val1,c:=Val1,d:=Val1,e:=Val1,
+ f:=Val1,g:=Val1,h:=Val1,i:=Val1,j:=Val1,
+ k:=Val1,l:=Val1,m:=Val1,n:=Val1,o:=Val1,
+ p:=Val1,q:=Val1,r:=Val1,s:=Val1,t:=Val1,
+ u:=Val1,v:=Val1,w:=Val1,x:=Val1,y:=Val1,
+ z:=Val1,
+ aa:=Val1,ab:=Val1,ac:=Val1,ad:=Val1,ae:=Val1,
+ af:=Val1,ag:=Val1,ah:=Val1,ai:=Val1,aj:=Val1,
+ ak:=Val1,al:=Val1,am:=Val1,an:=Val1,ao:=Val1,
+ ap:=Val1,aq:=Val1,ar:=Val1,as:=Val1,at:=Val1,
+ au:=Val1,av:=Val1,aw:=Val1,ax:=Val1,ay:=Val1,
+ az:=Val1,
+ K2=>[a,b,c]},
+
+ %% Traverse the maps to validate them.
+ _ = erlang:phash2({Map1,Map2}, 100000),
+
+ _ = {K1,K2,Val0,Val1}, %Force use of Y registers.
+ Map2.
+
+do_badmap(Test) ->
+ Terms = [Test,fun erlang:abs/1,make_ref(),self(),0.0/-1,
+ <<0:1024>>,<<1:1>>,<<>>,<<1,2,3>>,
+ [],{a,b,c},[a,b],atom,10.0,42,(1 bsl 65) + 3],
+ [Test(T) || T <- Terms].
diff --git a/lib/dialyzer/test/map_SUITE_data/src/map_in_guard.erl b/lib/dialyzer/test/map_SUITE_data/src/map_in_guard.erl
new file mode 100644
index 0000000000..6176ef1fdf
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/map_in_guard.erl
@@ -0,0 +1,35 @@
+-module(map_in_guard).
+
+-export([test/0, raw_expr/0]).
+
+test() ->
+ false = assoc_guard(#{}),
+ true = assoc_guard(not_a_map),
+ #{a := true} = assoc_update(#{}),
+ {'EXIT', {{badmap, not_a_map}, [{?MODULE, assoc_update, 1, _}|_]}}
+ = (catch assoc_update(not_a_map)),
+ ok = assoc_guard_clause(#{}),
+ {'EXIT', {function_clause, [{?MODULE, assoc_guard_clause, _, _}|_]}}
+ = (catch assoc_guard_clause(not_a_map)),
+ true = exact_guard(#{a=>1}),
+ {'EXIT', {function_clause, [{?MODULE, assoc_guard_clause, _, _}|_]}}
+ %% There's nothing we can do to find the error here, is there?
+ = (catch (begin true = exact_guard(#{}) end)),
+ ok = exact_guard_clause(#{a => q}),
+ {'EXIT', {function_clause, [{?MODULE, exact_guard_clause, _, _}|_]}}
+ = (catch exact_guard_clause(#{})),
+ ok.
+
+assoc_guard(M) when is_map(M#{a => b}) -> true;
+assoc_guard(Q) -> false.
+
+assoc_update(M) -> M#{a => true}.
+
+assoc_guard_clause(M) when is_map(M#{a => 3}) -> ok.
+
+exact_guard(M) when (false =/= M#{a := b}) -> true;
+exact_guard(_) -> false.
+
+exact_guard_clause(M) when (false =/= M#{a := b}) -> ok.
+
+raw_expr() when #{}; true -> ok. %% Must not warn here!
diff --git a/lib/dialyzer/test/map_SUITE_data/src/map_in_guard2.erl b/lib/dialyzer/test/map_SUITE_data/src/map_in_guard2.erl
new file mode 100644
index 0000000000..ac2205e8fa
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/map_in_guard2.erl
@@ -0,0 +1,27 @@
+-module(map_in_guard2).
+
+-export([test/0]).
+
+test() ->
+ false = assoc_guard(not_a_map),
+ {'EXIT', {{badmap, not_a_map}, [{?MODULE, assoc_update, 1, _}|_]}}
+ = (catch assoc_update(not_a_map)),
+ {'EXIT', {function_clause, [{?MODULE, assoc_guard_clause, _, _}|_]}}
+ = (catch assoc_guard_clause(not_a_map)),
+ {'EXIT', {function_clause, [{?MODULE, assoc_guard_clause, _, _}|_]}}
+ = (catch (begin true = exact_guard(#{}) end)),
+ {'EXIT', {function_clause, [{?MODULE, exact_guard_clause, _, _}|_]}}
+ = (catch exact_guard_clause(#{})),
+ ok.
+
+assoc_guard(M) when is_map(M#{a => b}) -> true;
+assoc_guard(_) -> false.
+
+assoc_update(M) -> M#{a => true}.
+
+assoc_guard_clause(M) when is_map(M#{a => 3}) -> ok.
+
+exact_guard(M) when (false =/= M#{a := b}) -> true;
+exact_guard(_) -> false.
+
+exact_guard_clause(M) when (false =/= M#{a := b}) -> ok.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/map_size.erl b/lib/dialyzer/test/map_SUITE_data/src/map_size.erl
new file mode 100644
index 0000000000..2da4f6904e
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/map_size.erl
@@ -0,0 +1,36 @@
+-module(map_size).
+
+-export([t1/0, e1/0, t2/0, t3/0, t4/0, t5/1, t6/1, t7/1]).
+
+t1() ->
+ 0 = maps:size(#{}),
+ 1 = maps:size(#{}).
+
+e1() ->
+ 0 = map_size(#{}),
+ 1 = map_size(#{}).
+
+t2() -> p(#{a=>x}).
+
+p(M) when map_size(M) =:= 0 -> ok.
+
+t3() ->
+ 1 = map_size(cio()),
+ 2 = map_size(cio()),
+ 3 = map_size(cio()),
+ 4 = map_size(cio()).
+
+t4() ->
+ 0 = map_size(cio()).
+
+t5(M) when map_size(M) =:= 0 ->
+ #{a := _} = M. %% Only t5 has no local return; want better message
+
+t6(M) when map_size(M) =:= 0 ->
+ #{} = M.
+
+t7(M=#{a := _}) when map_size(M) =:= 1 ->
+ #{b := _} = M. %% We should warn here too
+
+-spec cio() -> #{3 := ok, 9 => _, 11 => x}.
+cio() -> binary_to_term(<<131,116,0,0,0,2,97,3,100,0,2,111,107,97,9,97,6>>).
diff --git a/lib/dialyzer/test/map_SUITE_data/src/maps_merge.erl b/lib/dialyzer/test/map_SUITE_data/src/maps_merge.erl
new file mode 100644
index 0000000000..d4f3c6887a
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/maps_merge.erl
@@ -0,0 +1,29 @@
+-module(maps_merge).
+
+-export([t1/0, t2/0, t3/0, t4/0, t5/0]).
+
+t1() ->
+ #{a:=1} = maps:merge(#{}, #{}).
+
+t2() ->
+ #{hej := _} = maps:merge(cao(), cio()),
+ #{{} := _} = maps:merge(cao(), cio()).
+
+t3() ->
+ #{a:=1} = maps:merge(cao(), cio()),
+ #{7:=q} = maps:merge(cao(), cio()).
+
+t4() ->
+ #{a:=1} = maps:merge(cio(), cao()),
+ #{7:=q} = maps:merge(cio(), cao()).
+
+t5() ->
+ #{a:=2} = maps:merge(cao(), #{}).
+
+-spec cao() -> #{a := 1, q => none(), 11 => _, atom() => _}.
+cao() ->
+ binary_to_term(<<131,116,0,0,0,3,100,0,1,97,97,1,100,0,1,98,97,9,100,0,1,
+ 102,104,0>>).
+
+-spec cio() -> #{3 := ok, 7 => none(), z => _, integer() => _}.
+cio() -> binary_to_term(<<131,116,0,0,0,2,97,3,100,0,2,111,107,97,9,97,6>>).
diff --git a/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl b/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl
new file mode 100644
index 0000000000..b98c713c6b
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl
@@ -0,0 +1,69 @@
+-module(opaque_key_adt).
+
+-compile(export_all).
+
+-export_type([t/0, t/1, m/0, s/1, sm/1]).
+
+-opaque t() :: #{atom() => integer()}.
+-opaque t(A) :: #{A => integer()}.
+
+-opaque m() :: #{t() => integer()}.
+-type mt() :: #{t() => integer()}.
+
+-opaque s(K) :: #{K => integer(), integer() => atom()}.
+-opaque sm(K) :: #{K := integer(), integer() := atom()}.
+-type smt(K) :: #{K := integer(), integer() := atom()}.
+
+-spec t0() -> t().
+t0() -> #{}.
+
+-spec t1() -> t(integer()).
+t1() -> #{3 => 1}.
+
+-spec m0() -> m().
+m0() -> #{#{} => 3}.
+
+-spec mt0() -> mt().
+mt0() -> #{#{} => 3}.
+
+-spec s0() -> s(atom()).
+s0() -> #{}.
+
+-spec s1() -> s(atom()).
+s1() -> #{3 => a}.
+
+-spec s2() -> s(atom() | 3).
+s2() -> #{3 => a}. %% Contract breakage (not found)
+
+-spec s3() -> s(atom() | 3).
+s3() -> #{3 => 5, a => 6, 7 => 8}.
+
+-spec s4() -> s(integer()).
+s4() -> #{1 => a}. %% Contract breakage
+
+-spec s5() -> s(1).
+s5() -> #{2 => 3}. %% Contract breakage
+
+-spec s6() -> s(1).
+s6() -> #{1 => 3}.
+
+-spec s7() -> s(integer()).
+s7() -> #{1 => 3}.
+
+-spec sm1() -> sm(1).
+sm1() -> #{1 => 2, 3 => a}.
+
+-spec smt1() -> smt(1).
+smt1() -> #{3 => a}. %% Contract breakage
+
+-spec smt2() -> smt(1).
+smt2() -> #{1 => a}. %% Contract breakage
+
+-spec smt3() -> smt(q).
+smt3() -> #{q => 1}. %% Slight contract breakage (probably requires better map type)
+
+-spec smt4() -> smt(q).
+smt4() -> #{q => 2, 3 => a}.
+
+-spec smt5() -> smt(1).
+smt5() -> #{1 => 2, 3 => a}.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_use.erl b/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_use.erl
new file mode 100644
index 0000000000..917413fdd2
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_use.erl
@@ -0,0 +1,97 @@
+-module(opaque_key_use).
+
+-compile(export_all).
+
+-export_type([t/0, t/1]).
+
+-opaque t() :: #{atom() => integer()}.
+-opaque t(A) :: #{A => integer()}.
+
+tt1() ->
+ A = t0(),
+ B = t1(),
+ A =:= B. % never 'true'
+
+-spec t0() -> t().
+t0() -> #{a => 1}.
+
+-spec t1() -> t(integer()).
+t1() -> #{3 => 1}.
+
+adt_tt1() ->
+ A = adt_t0(),
+ B = adt_t1(),
+ A =:= B. % opaque attempt
+
+adt_tt2() ->
+ A = adt_t0(),
+ B = adt_t1(),
+ #{A => 1 % opaque key
+ ,B => 2 % opaque key
+ }.
+
+adt_tt3() ->
+ A = map_adt:t0(),
+ #{A => 1}. % opaque key
+
+adt_mm1() ->
+ A = adt_t0(),
+ M = adt_m0(),
+ #{A := R} = M, % opaque attempt
+ R.
+
+%% adt_ms1() ->
+%% A = adt_t0(),
+%% M = adt_m0(),
+%% M#{A}. % opaque arg
+
+adt_mu1() ->
+ A = adt_t0(),
+ M = adt_m0(),
+ M#{A := 4}. % opaque arg
+
+adt_mu2() ->
+ A = adt_t0(),
+ M = adt_m0(),
+ M#{A => 4}. % opaque arg
+
+adt_mu3() ->
+ M = adt_m0(),
+ M#{}. % opaque arg
+
+adt_mtm1() ->
+ A = adt_t0(),
+ M = adt_mt0(),
+ #{A := R} = M, % opaque key
+ R.
+
+%% adt_mts1() ->
+%% A = adt_t0(),
+%% M = adt_mt0(),
+%% M#{A}. % opaque key
+
+adt_mtu1() ->
+ A = adt_t0(),
+ M = adt_mt0(),
+ M#{A := 4}. % opaque key
+
+adt_mtu2() ->
+ A = adt_t0(),
+ M = adt_mt0(),
+ M#{A => 4}. % opaque key
+
+adt_mtu3() ->
+ M = adt_mt0(),
+ M#{}. % Ok to not warn
+
+adt_t0() ->
+ opaque_key_adt:t0().
+
+adt_t1() ->
+ opaque_key_adt:t1().
+
+adt_m0() ->
+ opaque_key_adt:m0().
+
+adt_mt0() ->
+ opaque_key_adt:mt0().
diff --git a/lib/dialyzer/test/map_SUITE_data/src/order.erl b/lib/dialyzer/test/map_SUITE_data/src/order.erl
new file mode 100644
index 0000000000..51868d7e94
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/order.erl
@@ -0,0 +1,56 @@
+-module(order).
+
+-export([t1/0, t2/0, t3/0, t4/0, t5/0, t6/0]).
+
+t1() ->
+ case maps:get(a, #{a=>1, a=>b}) of
+ Int when is_integer(Int) -> fail;
+ Atom when is_atom(Atom) -> error(ok);
+ _Else -> fail
+ end.
+
+t2() ->
+ case maps:get(a, #{a=>id_1(1), a=>id_b(b)}) of
+ Int when is_integer(Int) -> fail;
+ Atom when is_atom(Atom) -> error(ok);
+ _Else -> fail
+ end.
+
+t3() ->
+ case maps:get(a, #{a=>id_1(1), id_a(a)=>id_b(b)}) of
+ Int when is_integer(Int) -> fail;
+ Atom when is_atom(Atom) -> error(ok);
+ _Else -> fail
+ end.
+
+t4() ->
+ case maps:get(a, #{a=>id_1(1), a_or_b()=>id_b(b)}) of
+ Int when is_integer(Int) -> ok;
+ Atom when is_atom(Atom) -> ok;
+ _Else -> fail
+ end.
+
+t5() ->
+ case maps:get(c, #{c=>id_1(1), a_or_b()=>id_b(b)}) of
+ Int when is_integer(Int) -> error(ok);
+ Atom when is_atom(Atom) -> fail;
+ _Else -> fail
+ end.
+
+t6() ->
+ case maps:get(a, #{a_or_b()=>id_1(1), id_a(a)=>id_b(b)}) of
+ Int when is_integer(Int) -> fail;
+ Atom when is_atom(Atom) -> error(ok);
+ _Else -> fail
+ end.
+
+id_1(X) -> X.
+
+id_a(X) -> X.
+
+id_b(X) -> X.
+
+any() -> binary_to_term(<<>>).
+
+-spec a_or_b() -> a | b.
+a_or_b() -> any().
diff --git a/lib/dialyzer/test/map_SUITE_data/src/subtract_value_flip.erl b/lib/dialyzer/test/map_SUITE_data/src/subtract_value_flip.erl
new file mode 100644
index 0000000000..97e6b54e3c
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/subtract_value_flip.erl
@@ -0,0 +1,9 @@
+-module(subtract_value_flip).
+
+-export([t1/1]).
+
+t1(#{type := _Smth} = Map) ->
+ case Map of
+ #{type := a} -> ok;
+ #{type := b} -> error
+ end.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/typeflow.erl b/lib/dialyzer/test/map_SUITE_data/src/typeflow.erl
new file mode 100644
index 0000000000..b43fd6897b
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/typeflow.erl
@@ -0,0 +1,25 @@
+-module(typeflow).
+
+-export([t1/1, t2/1, t3/1, t4/1]).
+
+t1(M = #{}) ->
+ a_is_integer(M),
+ case M of
+ #{a := X} when is_integer(X) -> ok;
+ _ -> fail
+ end.
+
+a_is_integer(#{a := X}) when is_integer(X) -> ok.
+
+t2(M = #{}) ->
+ a_is_integer(M),
+ lists:sort(maps:get(a, M)),
+ ok.
+
+t3(M = #{}) ->
+ lists:sort(maps:get(a, M)),
+ ok.
+
+t4(M) ->
+ lists:sort(maps:get(a, M)),
+ ok.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/typeflow2.erl b/lib/dialyzer/test/map_SUITE_data/src/typeflow2.erl
new file mode 100644
index 0000000000..71a9657a60
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/typeflow2.erl
@@ -0,0 +1,88 @@
+-module(typeflow2).
+
+-export([t1/1, t2/1, t3/1, t4/1, t5/1, t6/1, t7/1, optional3/1]).
+
+t1(L) ->
+ M = only_integers_and_lists(L),
+ optional(M),
+ case M of
+ #{a := X} when is_integer(X) -> ok;
+ #{a := X} when is_list(X) -> ok; %% Must warn here
+ #{a := X} when is_pid(X) -> ok;
+ _ -> fail
+ end.
+
+optional(#{a:=X}) ->
+ true = is_integer(X);
+optional(#{}) ->
+ true.
+
+only_integers_and_lists(L) -> only_integers_and_lists(L, #{}).
+
+only_integers_and_lists([], M) -> M;
+only_integers_and_lists([{K,V}|T], M) when is_integer(V); is_list(V)->
+ only_integers_and_lists(T, M#{K => V}).
+
+t2(L) ->
+ M = only_integers_and_lists(L),
+ optional(M),
+ lists:sort(maps:get(a, M)),
+ ok.
+
+t3(L) ->
+ M = only_integers_and_lists(L),
+ lists:sort(maps:get(a, M)),
+ ok.
+
+t4(V) ->
+ M=map_with(a,V),
+ optional2(M),
+ case M of
+ #{a := X} when is_integer(X) -> ok;
+ #{a := X} when is_list(X) -> ok; %% Must warn here
+ _ -> fail
+ end.
+
+optional2(#{a:=X}) ->
+ true = is_integer(X);
+optional2(#{}) ->
+ true.
+
+map_with(K, V) when is_integer(V); is_list(V); is_atom(V) -> #{K => V}.
+
+t5(L) ->
+ M = only_integers_and_lists(L),
+ optional3(M),
+ case M of
+ #{a := X} when is_integer(X) -> ok;
+ #{a := X} when is_list(X) -> ok; %% Must warn here
+ #{a := X} when is_pid(X) -> ok; %% Must warn here
+ #{a := X} when is_atom(X) -> ok;
+ _ -> fail
+ end.
+
+t6(L) ->
+ M = only_integers_and_lists(L),
+ case M of
+ #{a := X} when is_integer(X) -> ok;
+ #{a := X} when is_list(X) -> ok; %% Must not warn here
+ _ -> fail
+ end.
+
+optional3(#{a:=X}) ->
+ true = is_integer(X);
+optional3(#{}) ->
+ true.
+
+t7(M) ->
+ optional4(M),
+ case M of
+ #{a := X} when is_integer(X) -> ok;
+ #{a := X} when is_list(X) -> ok;
+ #{a := X} when is_pid(X) -> ok; %% Must warn here
+ #{a := X} when is_atom(X) -> ok; %% Must warn here
+ _ -> fail %% Must not warn here (requires parsing)
+ end.
+
+-spec optional4(#{a=>integer()|list()}) -> true.
+optional4(#{}) -> true.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/typesig.erl b/lib/dialyzer/test/map_SUITE_data/src/typesig.erl
new file mode 100644
index 0000000000..b50511af41
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/typesig.erl
@@ -0,0 +1,9 @@
+-module(typesig).
+
+-export([t1/0, t2/0, t3/0, test/1]).
+
+t1() -> test(#{a=>1}).
+t2() -> test(#{a=>{b}}).
+t3() -> test(#{a=>{3}}).
+
+test(#{a:={X}}) -> X+1.
diff --git a/lib/dialyzer/test/small_SUITE_data/results/literals b/lib/dialyzer/test/small_SUITE_data/results/literals
index 222d2c0cdb..1ee39453a4 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/literals
+++ b/lib/dialyzer/test/small_SUITE_data/results/literals
@@ -5,6 +5,7 @@ literals.erl:14: Function t2/0 has no local return
literals.erl:15: Record construction #r{id::'a'} violates the declared type of field id::'integer'
literals.erl:17: Function t3/0 has no local return
literals.erl:18: Record construction #r{id::'a'} violates the declared type of field id::'integer'
+literals.erl:20: Function t4/0 has no local return
literals.erl:21: Record construction #r{id::'a'} violates the declared type of field id::'integer'
literals.erl:23: Function m1/1 has no local return
literals.erl:23: Matching of pattern {'r', 'a'} tagged with a record name violates the declared type of #r{id::'integer'}
@@ -12,3 +13,5 @@ literals.erl:26: Function m2/1 has no local return
literals.erl:26: Matching of pattern {'r', 'a'} tagged with a record name violates the declared type of #r{id::'integer'}
literals.erl:29: Function m3/1 has no local return
literals.erl:29: The pattern {{'r', 'a'}} can never match the type any()
+literals.erl:32: Function m4/1 has no local return
+literals.erl:32: Matching of pattern {'r', 'a'} tagged with a record name violates the declared type of #r{id::'integer'}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps1 b/lib/dialyzer/test/small_SUITE_data/results/maps1
index e88c91f21f..a178e96b20 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/maps1
+++ b/lib/dialyzer/test/small_SUITE_data/results/maps1
@@ -1,4 +1,4 @@
maps1.erl:43: Function t3/0 has no local return
-maps1.erl:44: The call maps1:foo(~{'greger'=>3, ~{'arne'=>'anka'}~=>45}~,1) will never return since it differs in the 2nd argument from the success typing arguments: (#{},'b')
-maps1.erl:52: The call Mod:'function'(~{'literal'=>'map'}~,'another_arg') requires that Mod is of type atom() not #{}
+maps1.erl:44: The call maps1:foo(#{'greger'=>3, #{'arne'=>'anka'}=>45},1) will never return since it differs in the 1st and 2nd argument from the success typing arguments: (#{'beta':=_, ...},'b')
+maps1.erl:52: The variable Mod can never match since previous clauses completely covered the type #{}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps_difftype b/lib/dialyzer/test/small_SUITE_data/results/maps_difftype
index 8980321135..3018b888db 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/maps_difftype
+++ b/lib/dialyzer/test/small_SUITE_data/results/maps_difftype
@@ -1,3 +1,3 @@
maps_difftype.erl:10: Function empty_mismatch/1 has no local return
-maps_difftype.erl:11: The pattern ~{}~ can never match the type tuple()
+maps_difftype.erl:11: The pattern #{} can never match the type tuple()
diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps_sum b/lib/dialyzer/test/small_SUITE_data/results/maps_sum
index a19c0bba96..bd192bdb93 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/maps_sum
+++ b/lib/dialyzer/test/small_SUITE_data/results/maps_sum
@@ -1,4 +1,4 @@
-maps_sum.erl:15: Invalid type specification for function maps_sum:wrong1/1. The success typing is (#{}) -> any()
+maps_sum.erl:15: Invalid type specification for function maps_sum:wrong1/1. The success typing is (map()) -> any()
maps_sum.erl:26: Function wrong2/1 has no local return
maps_sum.erl:27: The call lists:foldl(fun((_,_,_) -> any()),0,Data::any()) will never return since it differs in the 1st argument from the success typing arguments: (fun((_,_) -> any()),any(),[any()])
diff --git a/lib/dialyzer/test/small_SUITE_data/src/literals.erl b/lib/dialyzer/test/small_SUITE_data/src/literals.erl
index abd7033712..354a0f4cdc 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/literals.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/literals.erl
@@ -2,7 +2,7 @@
%% Bad records inside structures used to be ignored. The reason:
%% v3_core:unfold() does not annotate the parts of a literal.
-%% This example does not work perfectly yet, in particular Maps.
+%% This example does not work perfectly yet.
-export([t1/0, t2/0, t3/0, t4/0, m1/1, m2/1, m3/1, m4/1]).
@@ -18,7 +18,7 @@ t3() ->
{#r{id = a}}. % violation
t4() ->
- #{a => #r{id = a}}. % violation found, but t4() returns... (bug)
+ #{a => #r{id = a}}. % violation
m1(#r{id = a}) -> % violation
ok.
@@ -29,5 +29,5 @@ m2([#r{id = a}]) -> % violation
m3({#r{id = a}}) -> % can never match; not so good
ok.
-m4(#{a := #r{id = a}}) -> % violation not found
+m4(#{a := #r{id = a}}) -> % violation
ok.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
index bb2f66a498..597358d16a 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
@@ -21,7 +21,7 @@ recv(Packet, Fun, Chan) ->
#{id := Can_id, data := Can_data} = P = decode(Packet),
Fun(P).
--spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char()}.
+-spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char(),_ => _}.
decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18,
Data:Len/binary, _/binary>>) ->
#{id => Id, data => Data, timestamp => Timestamp}.
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 44982ab46d..77ea9d0413 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.9
+DIALYZER_VSN = 2.10
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index de88f6befd..e8f2f63f86 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -36,6 +36,8 @@
%% Information.
-export([services/0,
+ peer_info/1,
+ peer_find/1,
service_info/2]).
%% Start/stop the application. In a "real" application this should
@@ -53,6 +55,7 @@
service_name/0,
capability/0,
peer_filter/0,
+ peer_ref/0,
service_opt/0,
application_opt/0,
app_module/0,
@@ -147,6 +150,27 @@ service_info(SvcName, Option) ->
diameter_service:info(SvcName, Option).
%% ---------------------------------------------------------------------------
+%% peer_info/2
+%% ---------------------------------------------------------------------------
+
+-spec peer_info(peer_ref())
+ -> [tuple()].
+
+peer_info(PeerRef) ->
+ diameter_service:peer_info(PeerRef).
+
+%% ---------------------------------------------------------------------------
+%% peer_find/1
+%% ---------------------------------------------------------------------------
+
+-spec peer_find(peer_ref() | pid())
+ -> {peer_ref(), pid()}
+ | false.
+
+peer_find(Pid) ->
+ diameter_peer_fsm:find(Pid).
+
+%% ---------------------------------------------------------------------------
%% add_transport/3
%% ---------------------------------------------------------------------------
@@ -280,6 +304,9 @@ call(SvcName, App, Message) ->
| {all, [peer_filter()]}
| {any, [peer_filter()]}.
+-opaque peer_ref()
+ :: pid().
+
-type evaluable()
:: {module(), atom(), list()}
| fun()
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index fb874013a3..996e75a8d3 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,6 +32,9 @@
-export([start/3,
result_code/2]).
+%% Interface towards diameter.
+-export([find/1]).
+
%% gen_server callbacks
-export([init/1,
handle_call/3,
@@ -185,6 +188,25 @@ start_link(T) ->
infinity,
diameter_lib:spawn_opts(server, [])).
+%% find/1
+%%
+%% Identify both pids of a peer_fsm/transport pair.
+
+find(Pid) ->
+ findl([{?MODULE, '_', Pid}, {?MODULE, Pid, '_'}]).
+
+findl([]) ->
+ false;
+
+findl([Pat | Rest]) ->
+ try
+ [{{_, Pid, TPid}, Pid}] = diameter_reg:match(Pat),
+ {Pid, TPid}
+ catch
+ error:_ ->
+ findl(Rest)
+ end.
+
%% ---------------------------------------------------------------------------
%% ---------------------------------------------------------------------------
@@ -215,6 +237,8 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
{TPid, Addrs} = start_transport(T, Rest, Svc),
+ diameter_reg:add({?MODULE, self(), TPid}), %% lets pairs be discovered
+
#state{state = {'Wait-Conn-Ack', Tmo},
parent = WPid,
transport = TPid,
@@ -416,8 +440,8 @@ transition({connection_timeout, _}, _) ->
ok;
%% Incoming message from the transport.
-transition({diameter, {recv, Pkt}}, S) ->
- recv(Pkt, S);
+transition({diameter, {recv, MsgT}}, S) ->
+ incoming(MsgT, S);
%% Timeout when still in the same state ...
transition({timeout = T, PS}, #state{state = PS}) ->
@@ -543,6 +567,28 @@ encode(Rec, Dict) ->
diameter_codec:encode(Dict, #diameter_packet{header = Hdr,
msg = Rec}).
+%% incoming/2
+
+incoming({Msg, NPid}, S) ->
+ try recv(Msg, S) of
+ T ->
+ NPid ! {diameter, discard},
+ T
+ catch
+ {?MODULE, Name, Pkt} ->
+ S#state.parent ! {recv, self(), Name, {Pkt, NPid}},
+ rcv(Name, Pkt, S)
+ end;
+
+incoming(Msg, S) ->
+ try
+ recv(Msg, S)
+ catch
+ {?MODULE, Name, Pkt} ->
+ S#state.parent ! {recv, self(), Name, Pkt},
+ rcv(Name, Pkt, S)
+ end.
+
%% recv/2
recv(#diameter_packet{header = #diameter_header{} = Hdr}
@@ -597,9 +643,8 @@ recv1('DPA' = N,
%% Any other message with a header and no length errors: send to the
%% parent.
-recv1(Name, Pkt, #state{parent = Pid} = S) ->
- Pid ! {recv, self(), Name, Pkt},
- rcv(Name, Pkt, S).
+recv1(Name, Pkt, #state{}) ->
+ throw({?MODULE, Name, Pkt}).
%% recv/3
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index efa4cc9108..cfb5cb5b82 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -32,6 +32,7 @@
-export([subscribe/1,
unsubscribe/1,
services/0,
+ peer_info/1,
info/2]).
%% towards diameter_config
@@ -218,6 +219,29 @@ lookup_state(SvcName) ->
end.
%% ---------------------------------------------------------------------------
+%% # peer_info/2
+%% ---------------------------------------------------------------------------
+
+%% An extended version of info_peer/1 for peer_info/1.
+peer_info(Pid) ->
+ try
+ {_, PD} = process_info(Pid, dictionary),
+ {_, T} = lists:keyfind({diameter_peer_fsm, start}, 1, PD),
+ {TPid, {{Type, Ref}, TMod, Cfg}} = T,
+ {_, TD} = process_info(TPid, dictionary),
+ {_, Data} = lists:keyfind({TMod, info}, 1, TD),
+ [{ref, Ref},
+ {type, Type},
+ {owner, TPid},
+ {module, TMod},
+ {config, Cfg}
+ | try TMod:info(Data) catch _:_ -> [] end]
+ catch
+ error:_ ->
+ []
+ end.
+
+%% ---------------------------------------------------------------------------
%% # subscribe/1
%% # unsubscribe/1
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index c169d3fc2c..2112941d5e 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -230,7 +230,15 @@ pending(TPids) ->
%% used to come through the service process but this avoids that
%% becoming a bottleneck.
-receive_message(TPid, Pkt, Dict0, RecvData)
+receive_message(TPid, {Pkt, NPid}, Dict0, RecvData) ->
+ NPid ! {diameter, incoming(TPid, Pkt, Dict0, RecvData)};
+
+receive_message(TPid, Pkt, Dict0, RecvData) ->
+ incoming(TPid, Pkt, Dict0, RecvData).
+
+%% incoming/4
+
+incoming(TPid, Pkt, Dict0, RecvData)
when is_pid(TPid) ->
#diameter_packet{header = #diameter_header{is_request = R}} = Pkt,
recv(R,
@@ -244,11 +252,18 @@ receive_message(TPid, Pkt, Dict0, RecvData)
%% Incoming request ...
recv(true, false, TPid, Pkt, Dict0, T) ->
- spawn_request(TPid, Pkt, Dict0, T);
+ try
+ {request, spawn_request(TPid, Pkt, Dict0, T)}
+ catch
+ error: system_limit = E -> %% discard
+ ?LOG(error, E),
+ discard
+ end;
%% ... answer to known request ...
recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) ->
- Pid ! {answer, Ref, Req, Dict0, Pkt};
+ Pid ! {answer, Ref, Req, Dict0, Pkt},
+ {answer, Pid};
%% Note that failover could have happened prior to this message being
%% received and triggering failback. That is, both a failover message
@@ -263,7 +278,7 @@ recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) ->
recv(false, false, TPid, Pkt, _, _) ->
?LOG(discarded, Pkt#diameter_packet.header),
incr(TPid, {{unknown, 0}, recv, discarded}),
- ok.
+ discard.
%% spawn_request/4
@@ -273,12 +288,7 @@ spawn_request(TPid, Pkt, Dict0, RecvData) ->
spawn_request(TPid, Pkt, Dict0, ?DEFAULT_SPAWN_OPTS, RecvData).
spawn_request(TPid, Pkt, Dict0, Opts, RecvData) ->
- try
- spawn_opt(fun() -> recv_request(TPid, Pkt, Dict0, RecvData) end, Opts)
- catch
- error: system_limit = E -> %% discard
- ?LOG(error, E)
- end.
+ spawn_opt(fun() -> recv_request(TPid, Pkt, Dict0, RecvData) end, Opts).
%% ---------------------------------------------------------------------------
%% recv_request/4
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index ea8b2fdb0e..3fd87b223e 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -449,8 +449,14 @@ transition({'DOWN', _, process, TPid, _Reason} = D,
end;
%% Incoming message.
-transition({recv, TPid, Name, Pkt}, #watchdog{transport = TPid} = S) ->
- recv(Name, Pkt, S);
+transition({recv, TPid, Name, PktT}, #watchdog{transport = TPid} = S) ->
+ try
+ incoming(Name, PktT, S)
+ catch
+ #watchdog{dictionary = Dict0, receive_data = T} = NS ->
+ diameter_traffic:receive_message(TPid, PktT, Dict0, T),
+ NS
+ end;
%% Current watchdog has timed out.
transition({timeout, TRef, tw}, #watchdog{tref = TRef} = S) ->
@@ -578,22 +584,32 @@ send_watchdog(#watchdog{pending = false,
%% Don't count encode errors since we don't expect any on DWR/DWA.
+%% incoming/3
+
+incoming(Name, {Pkt, NPid}, S) ->
+ NS = recv(Name, Pkt, S),
+ NPid ! {diameter, discard},
+ NS;
+
+incoming(Name, Pkt, S) ->
+ recv(Name, Pkt, S).
+
%% recv/3
recv(Name, Pkt, S) ->
- try rcv(Name, S) of
+ try rcv(Name, Pkt, rcv(Name, S)) of
#watchdog{} = NS ->
- rcv(Name, Pkt, S),
- NS
+ throw(NS)
catch
- {?MODULE, throwaway, #watchdog{} = NS} ->
+ #watchdog{} = NS -> %% throwaway
NS
end.
%% rcv/3
rcv('DWR', Pkt, #watchdog{transport = TPid,
- dictionary = Dict0}) ->
+ dictionary = Dict0}
+ = S) ->
?LOG(recv, 'DWR'),
DPkt = diameter_codec:decode(Dict0, Pkt),
diameter_traffic:incr(recv, DPkt, TPid, Dict0),
@@ -610,32 +626,30 @@ rcv('DWR', Pkt, #watchdog{transport = TPid,
send(TPid, {send, #diameter_packet{header = H,
transport_data = T,
bin = Bin}}),
- ?LOG(send, 'DWA');
+ ?LOG(send, 'DWA'),
+ throw(S);
rcv('DWA', Pkt, #watchdog{transport = TPid,
- dictionary = Dict0}) ->
+ dictionary = Dict0}
+ = S) ->
?LOG(recv, 'DWA'),
diameter_traffic:incr(recv, Pkt, TPid, Dict0),
diameter_traffic:incr_rc(recv,
diameter_codec:decode(Dict0, Pkt),
TPid,
- Dict0);
+ Dict0),
+ throw(S);
-rcv(N, _, _)
+rcv(N, _, S)
when N == 'CER';
N == 'CEA';
N == 'DPR' ->
- false;
+ throw(S);
%% DPR can be sent explicitly with diameter:call/4. Only the
%% corresponding DPAs arrive here.
-rcv(_, Pkt, #watchdog{transport = TPid,
- dictionary = Dict0,
- receive_data = T}) ->
- diameter_traffic:receive_message(TPid, Pkt, Dict0, T).
-
-throwaway(S) ->
- throw({?MODULE, throwaway, S}).
+rcv(_, _, S)->
+ S.
%% rcv/2
%%
@@ -652,20 +666,20 @@ throwaway(S) ->
%% INITIAL Receive non-DWA Throwaway() INITIAL
rcv('DWA', #watchdog{status = initial} = S) ->
- throwaway(S#watchdog{pending = false});
+ throw(S#watchdog{pending = false});
rcv(_, #watchdog{status = initial} = S) ->
- throwaway(S);
+ throw(S);
%% DOWN Receive DWA Pending = FALSE
%% Throwaway() DOWN
%% DOWN Receive non-DWA Throwaway() DOWN
rcv('DWA', #watchdog{status = down} = S) ->
- throwaway(S#watchdog{pending = false});
+ throw(S#watchdog{pending = false});
rcv(_, #watchdog{status = down} = S) ->
- throwaway(S);
+ throw(S);
%% OKAY Receive DWA Pending = FALSE
%% SetWatchdog() OKAY
@@ -721,7 +735,7 @@ rcv('DWR', #watchdog{status = reopen} = S) ->
S; %% ensure DWA: the RFC isn't explicit about answering
rcv(_, #watchdog{status = reopen} = S) ->
- throwaway(S).
+ throw(S).
%% timeout/1
%%
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index c79d85820b..6a5e5fe89d 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
%%
-module(diameter_tcp).
+-dialyzer({no_fail_call, throttle/2}).
-behaviour(gen_server).
@@ -102,7 +103,8 @@
| gen_tcp:listen_option().
-type option() :: {port, non_neg_integer()}
- | {fragment_timer, 0..16#FFFFFFFF}.
+ | {fragment_timer, 0..16#FFFFFFFF}
+ | {throttle_cb, diameter:evaluable()}.
%% Accepting/connecting transport process state.
-record(transport,
@@ -110,10 +112,13 @@
parent :: pid(), %% of process that started us
module :: module(), %% gen_tcp-like module
frag = <<>> :: frag(), %% message fragment
- ssl :: boolean() | [term()], %% ssl options
+ ssl :: [term()] | boolean(), %% ssl options, ssl or not
timeout :: infinity | 0..16#FFFFFFFF, %% fragment timeout
tref = false :: false | reference(), %% fragment timer reference
- flush = false :: boolean()}). %% flush fragment at timeout?
+ flush = false :: boolean(), %% flush fragment at timeout?
+ throttle_cb :: false | diameter:evaluable(), %% ask to receive
+ throttled :: boolean() | binary()}). %% stopped receiving?
+
%% The usual transport using gen_tcp can be replaced by anything
%% sufficiently gen_tcp-like by passing a 'module' option as the first
%% (for simplicity) transport option. The transport_module diameter_etcp
@@ -198,22 +203,27 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
%% that does nothing but kill us with the parent until call
%% returns.
{ok, MPid} = diameter_tcp_sup:start_child(#monitor{parent = Pid}),
- {SslOpts, Rest0} = ssl(Opts),
- {OwnOpts, Rest} = own(Rest0),
+ {[SO|TO], Rest} = proplists:split(Opts, [ssl_options,
+ fragment_timer,
+ throttle_cb]),
+ SslOpts = ssl_opts(SO),
+ OwnOpts = lists:append(TO),
Tmo = proplists:get_value(fragment_timer,
OwnOpts,
?DEFAULT_FRAGMENT_TIMEOUT),
?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}),
+ Throttle = proplists:get_value(throttle_cb, OwnOpts, false),
Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs),
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
- setopts(M, Sock),
putr(?REF_KEY, Ref),
- #transport{parent = Pid,
- module = M,
- socket = Sock,
- ssl = SslOpts,
- timeout = Tmo};
+ throttle(#transport{parent = Pid,
+ module = M,
+ socket = Sock,
+ ssl = SslOpts,
+ timeout = Tmo,
+ throttle_cb = Throttle,
+ throttled = false /= Throttle});
%% Put the reference in the process dictionary since we now use it
%% advertise the ssl socket after TLS upgrade.
@@ -246,14 +256,6 @@ laddr([], Mod, Sock) ->
laddr([{ip, Addr}], _, _) ->
Addr.
-own(Opts) ->
- {[Own], Rest} = proplists:split(Opts, [fragment_timer]),
- {Own, Rest}.
-
-ssl(Opts) ->
- {[SslOpts], Rest} = proplists:split(Opts, [ssl_options]),
- {ssl_opts(SslOpts), Rest}.
-
ssl_opts([]) ->
false;
ssl_opts([{ssl_options, true}]) ->
@@ -261,8 +263,8 @@ ssl_opts([{ssl_options, true}]) ->
ssl_opts([{ssl_options, Opts}])
when is_list(Opts) ->
Opts;
-ssl_opts(L) ->
- ?ERROR({ssl_options, L}).
+ssl_opts(T) ->
+ ?ERROR({ssl_options, T}).
%% init/7
@@ -393,7 +395,7 @@ get_port(Ps) ->
gen_opts(LAddrOpt, Opts) ->
{L,_} = proplists:split(Opts, [binary, packet, active]),
[[],[],[]] == L orelse ?ERROR({reserved_options, Opts}),
- [binary, {packet, 0}, {active, once}] ++ LAddrOpt ++ Opts.
+ [binary, {packet, 0}, {active, false}] ++ LAddrOpt ++ Opts.
%% ---------------------------------------------------------------------------
%% # ports/1
@@ -536,53 +538,37 @@ t(T,S) ->
S;
#transport{} = NS ->
NS;
- {stop, Reason} ->
- x(Reason);
stop ->
x(T)
end.
%% transition/2
-%% Initial incoming message when we might need to upgrade to TLS:
-%% don't request another message until we know.
-
-transition({tcp, Sock, Bin}, #transport{socket = Sock,
- parent = Pid,
- frag = Head,
- module = M,
- ssl = Opts}
- = S)
- when is_list(Opts) ->
- case rcv(Head, Bin) of
- {Msg, B} when is_binary(Msg) ->
- diameter_peer:recv(Pid, Msg),
- S#transport{frag = B};
- Frag ->
- setopts(M, Sock),
- start_fragment_timer(S#transport{frag = Frag})
- end;
-
%% Incoming message.
transition({P, Sock, Bin}, #transport{socket = Sock,
- module = M,
- ssl = B}
+ ssl = B,
+ throttled = T}
= S)
- when P == tcp, not B;
- P == ssl, B ->
- setopts(M, Sock),
- start_fragment_timer(recv(Bin, S));
+ when P == ssl, true == B;
+ P == tcp ->
+ false = T, %% assert
+ recv(Bin, S);
+
+%% Make a new throttling callback after a timeout.
+transition(throttle, #transport{throttled = false}) ->
+ ok;
+transition(throttle, S) ->
+ throttle(S);
%% Capabilties exchange has decided on whether or not to run over TLS.
transition({diameter, {tls, Ref, Type, B}}, #transport{parent = Pid}
= S) ->
- #transport{socket = Sock,
- module = M}
+ true = is_boolean(B), %% assert
+ #transport{}
= NS
= tls_handshake(Type, B, S),
Pid ! {diameter, {tls, Ref}},
- setopts(M, Sock),
- start_fragment_timer(NS#transport{ssl = B});
+ throttle(NS#transport{ssl = B});
transition({C, Sock}, #transport{socket = Sock,
ssl = B})
@@ -598,14 +584,8 @@ transition({E, Sock, _Reason} = T, #transport{socket = Sock,
?ERROR({T,S});
%% Outgoing message.
-transition({diameter, {send, Bin}}, #transport{socket = Sock,
- module = M}) ->
- case send(M, Sock, Bin) of
- ok ->
- ok;
- {error, Reason} ->
- {stop, {send, Reason}}
- end;
+transition({diameter, {send, Bin}}, S) ->
+ send(Bin, S);
%% Request to close the transport connection.
transition({diameter, {close, Pid}}, #transport{parent = Pid,
@@ -672,16 +652,25 @@ tls(accept, Sock, Opts) ->
%% Reassemble fragmented messages and extract multiple message sent
%% using Nagle.
-recv(Bin, #transport{parent = Pid, frag = Head} = S) ->
+%% Receive packets until a full message is received,
+recv(Bin, #transport{frag = Head, throttled = false} = S) ->
case rcv(Head, Bin) of
- {Msg, B} when is_binary(Msg) ->
- diameter_peer:recv(Pid, Msg),
- recv(B, S#transport{frag = <<>>});
+ {Msg, B} ->
+ throttle(S#transport{frag = B, throttled = Msg});
Frag ->
- S#transport{frag = Frag,
- flush = false}
+ setopts(S),
+ start_fragment_timer(S#transport{frag = Frag,
+ flush = false})
end.
+%% recv/1
+
+recv(#transport{throttled = false} = S) ->
+ recv(<<>>, S);
+
+recv(#transport{} = S) ->
+ S.
+
%% rcv/2
%% No previous fragment.
@@ -765,8 +754,10 @@ bin(Bin)
%% since all messages with length problems are discarded this should
%% also eventually lead to watchdog failover.
-%% No fragment to flush.
-flush(#transport{frag = <<>>} = S) ->
+%% No fragment to flush or not receiving messages.
+flush(#transport{frag = Frag, throttled = B} = S)
+ when Frag == <<>>;
+ B /= false ->
S;
%% Messages have been received since last timer expiry.
@@ -807,6 +798,17 @@ accept(Mod, LSock) ->
connect(Mod, Host, Port, Opts) ->
Mod:connect(Host, Port, Opts).
+%% send/2
+
+send(Bin, #transport{socket = Sock,
+ module = M}) ->
+ case send(M, Sock, Bin) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ x({send, Reason})
+ end.
+
%% send/3
send(gen_tcp, Sock, Bin) ->
@@ -825,6 +827,11 @@ setopts(ssl, Sock, Opts) ->
setopts(M, Sock, Opts) ->
M:setopts(Sock, Opts).
+%% setopts/1
+
+setopts(#transport{socket = Sock, module = M}) ->
+ setopts(M, Sock).
+
%% setopts/2
setopts(M, Sock) ->
@@ -833,6 +840,110 @@ setopts(M, Sock) ->
X -> x({setopts, M, Sock, X}) %% possibly on peer disconnect
end.
+%% throttle/1
+
+%% Still collecting packets for a complete message: keep receiving.
+throttle(#transport{throttled = false} = S) ->
+ recv(S);
+
+%% Decide whether to receive another, or whether to accept a message
+%% that's been received.
+throttle(#transport{throttle_cb = F, throttled = T} = S) ->
+ Res = cb(F, T),
+
+ try throttle(Res, S) of
+ #transport{ssl = SB} = NS when is_boolean(SB) ->
+ throttle(defrag(NS));
+ #transport{throttled = Msg} = NS when is_binary(Msg) ->
+ %% Initial incoming message when we might need to upgrade
+ %% to TLS: wait for reception of a tls tuple.
+ defrag(NS)
+ catch
+ #transport{} = NS ->
+ recv(NS)
+ end.
+
+%% cb/2
+
+cb(false, _) ->
+ ok;
+
+cb(F, B) ->
+ diameter_lib:eval([F, true /= B andalso B]).
+
+%% throttle/2
+
+%% Callback says to receive another message.
+throttle(ok, #transport{throttled = true} = S) ->
+ throw(S#transport{throttled = false});
+
+%% Callback says to accept a received message.
+throttle(ok, #transport{parent = Pid, throttled = Msg} = S)
+ when is_binary(Msg) ->
+ diameter_peer:recv(Pid, Msg),
+ S;
+
+throttle({ok = T, F}, S) ->
+ throttle(T, S#transport{throttle_cb = F});
+
+%% Callback says to accept a received message and acknowledged the
+%% returned pid with a {request, Pid} message if a request pid is
+%% spawned, a discard message otherwise. The latter does not mean that
+%% the message was necessarily discarded: it could have been an
+%% answer.
+throttle(NPid, #transport{parent = Pid, throttled = Msg} = S)
+ when is_pid(NPid), is_binary(Msg) ->
+ diameter_peer:recv(Pid, {Msg, NPid}),
+ S;
+
+throttle({NPid, F}, #transport{throttled = Msg} = S)
+ when is_pid(NPid), is_binary(Msg) ->
+ throttle(NPid, S#transport{throttle_cb = F});
+
+%% Callback to accept a received message says to discard it.
+throttle(discard, #transport{throttled = Msg} = S)
+ when is_binary(Msg) ->
+ S;
+
+throttle({discard = T, F}, #transport{throttled = Msg} = S)
+ when is_binary(Msg) ->
+ throttle(T, S#transport{throttle_cb = F});
+
+%% Callback to accept a received message says to answer it with the
+%% supplied binary.
+throttle(Bin, #transport{throttled = Msg} = S)
+ when is_binary(Bin), is_binary(Msg) ->
+ send(Bin, S),
+ S;
+
+throttle({Bin, F}, #transport{throttled = Msg} = S)
+ when is_binary(Bin), is_binary(Msg) ->
+ throttle(Bin, S#transport{throttle_cb = F});
+
+%% Callback says to ask again in the specified number of milliseconds.
+throttle({timeout, Tmo}, S) ->
+ erlang:send_after(Tmo, self(), throttle),
+ throw(S);
+
+throttle({timeout = T, Tmo, F}, S) ->
+ throttle({T, Tmo}, S#transport{throttle_cb = F});
+
+throttle(T, #transport{throttle_cb = F}) ->
+ ?ERROR({invalid_return, T, F}).
+
+%% defrag/1
+%%
+%% Try to extract another message from packets already read before
+%% another throttling callback.
+
+defrag(#transport{frag = Head} = S) ->
+ case rcv(Head, <<>>) of
+ {Msg, B} ->
+ S#transport{throttled = Msg, frag = B};
+ _ ->
+ S#transport{throttled = true}
+ end.
+
%% portnr/2
portnr(gen_tcp, Sock) ->
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 3784bf20a6..1ad897dcd2 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -327,10 +327,10 @@ make_cert(Dir, Keyfile, Certfile) ->
"-subj /C=SE/ST=./L=Stockholm/CN=www.erlang.org"]),
%% Hope for the best and only check that files are written.
- [{_, _, {ok,_}},{_, _, {ok,_}}]
- = [{P,O,T} || {P,C} <- [{KP,KC}, {CP,CC}],
- O <- [os:cmd(C)],
- T <- [file:read_file_info(P)]],
+ KR = os:cmd(KC),
+ {_, {ok, _}} = {KR, file:read_file_info(KP)},
+ CR = os:cmd(CC),
+ {_, {ok, _}} = {CR, file:read_file_info(CP)},
{KP,CP}.
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index d2494b69fe..94013bb5ac 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -653,20 +653,7 @@ find_invalid_unicode([]) -> none.
parse_file(Epp) ->
case scan_and_parse(Epp) of
{ok, Form} ->
- case Form of
- {attribute,La,record,{Record, Fields}} ->
- case epp:normalize_typed_record_fields(Fields) of
- {typed, NewFields} ->
- [{attribute, La, record, {Record, NewFields}},
- {attribute, La, type,
- {{record, Record}, Fields, []}}
- | parse_file(Epp)];
- not_typed ->
- [Form | parse_file(Epp)]
- end;
- _ ->
- [Form | parse_file(Epp)]
- end;
+ [Form | parse_file(Epp)];
{error, E} ->
[{error, E} | parse_file(Epp)];
{eof, Location} ->
diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl
index 758750083d..e7a4c36ca4 100644
--- a/lib/edoc/src/edoc_extract.erl
+++ b/lib/edoc/src/edoc_extract.erl
@@ -355,6 +355,8 @@ preprocess_forms_2(F, Fs) ->
[F | preprocess_forms_1(Fs)];
text ->
[F | preprocess_forms_1(Fs)];
+ {attribute, {record, _}} ->
+ [F | preprocess_forms_1(Fs)];
{attribute, {N, _}} ->
case edoc_specs:is_tag(N) of
true ->
@@ -373,50 +375,62 @@ preprocess_forms_2(F, Fs) ->
%% in the list.
collect(Fs, Mod) ->
- collect(Fs, [], [], [], [], undefined, Mod).
+ collect(Fs, [], [], [], [], [], undefined, Mod).
-collect([F | Fs], Cs, Ss, Ts, As, Header, Mod) ->
+collect([F | Fs], Cs, Ss, Ts, Rs, As, Header, Mod) ->
case erl_syntax_lib:analyze_form(F) of
comment ->
- collect(Fs, [F | Cs], Ss, Ts, As, Header, Mod);
+ collect(Fs, [F | Cs], Ss, Ts, Rs, As, Header, Mod);
{function, Name} ->
L = erl_syntax:get_pos(F),
Export = ordsets:is_element(Name, Mod#module.exports),
Args = parameters(erl_syntax:function_clauses(F)),
- collect(Fs, [], [], [],
+ collect(Fs, [], [], [], [],
[#entry{name = Name, args = Args, line = L,
export = Export,
- data = {comment_text(Cs),Ss,Ts}} | As],
+ data = {comment_text(Cs),Ss,Ts,Rs}} | As],
Header, Mod);
{attribute, {module, _}} when Header =:= undefined ->
L = erl_syntax:get_pos(F),
- collect(Fs, [], [], [], As,
+ collect(Fs, [], [], [], [], As,
#entry{name = module, line = L,
- data = {comment_text(Cs),Ss,Ts}},
+ data = {comment_text(Cs),Ss,Ts,Rs}},
Mod);
+ {attribute, {record, {_Name, Fields}}} ->
+ case is_typed_record(Fields) of
+ true ->
+ collect(Fs, Cs, Ss, Ts, [F | Rs], As, Header, Mod);
+ false ->
+ collect(Fs, Cs, Ss, Ts, Rs, As, Header, Mod)
+ end;
{attribute, {N, _}} ->
case edoc_specs:tag(N) of
spec ->
- collect(Fs, Cs, [F | Ss], Ts, As, Header, Mod);
+ collect(Fs, Cs, [F | Ss], Ts, Rs, As, Header, Mod);
type ->
- collect(Fs, Cs, Ss, [F | Ts], As, Header, Mod);
+ collect(Fs, Cs, Ss, [F | Ts], Rs, As, Header, Mod);
unknown ->
%% Drop current seen comments.
- collect(Fs, [], [], [], As, Header, Mod)
+ collect(Fs, [], [], [], Rs, As, Header, Mod)
end;
_ ->
%% Drop current seen comments.
- collect(Fs, [], [], [], As, Header, Mod)
+ collect(Fs, [], [], [], [], As, Header, Mod)
end;
-collect([], Cs, Ss, Ts, As, Header, _Mod) ->
- Footer = #entry{name = footer, data = {comment_text(Cs),Ss,Ts}},
+collect([], Cs, Ss, Ts, Rs, As, Header, _Mod) ->
+ Footer = #entry{name = footer, data = {comment_text(Cs),Ss,Ts,Rs}},
As1 = lists:reverse(As),
if Header =:= undefined ->
- {#entry{name = module, data = {[],[],[]}}, Footer, As1};
+ {#entry{name = module, data = {[],[],[],[]}}, Footer, As1};
true ->
{Header, Footer, As1}
end.
+is_typed_record([]) ->
+ false;
+is_typed_record([{_, {_, Type}} | Fs]) ->
+ Type =/= none orelse is_typed_record(Fs).
+
%% Returns a list of simplified comment information (position and text)
%% for a list of abstract comments. The order of elements is reversed.
@@ -549,8 +563,8 @@ get_tags(Es, Env, File, TypeDocs) ->
How = dict:from_list(edoc_tags:tag_parsers()),
get_tags(Es, Tags, Env, How, File, TypeDocs).
-get_tags([#entry{name = Name, data = {Cs,Specs,Types}} = E | Es], Tags, Env,
- How, File, TypeDocs) ->
+get_tags([#entry{name = Name, data = {Cs,Specs,Types,Records}} = E | Es],
+ Tags, Env, How, File, TypeDocs) ->
Where = {File, Name},
Ts0 = scan_tags(Cs),
{Ts1,Specs1} = select_spec(Ts0, Where, Specs),
@@ -558,7 +572,7 @@ get_tags([#entry{name = Name, data = {Cs,Specs,Types}} = E | Es], Tags, Env,
Ts3 = edoc_macros:expand_tags(Ts2, Env, Where),
Ts4 = edoc_tags:parse_tags(Ts3, How, Env, Where),
Ts = selected_specs(Specs1, Ts4),
- ETypes = [edoc_specs:type(Type, TypeDocs) || Type <- Types],
+ ETypes = [edoc_specs:type(Type, TypeDocs) || Type <- Types ++ Records],
[E#entry{data = Ts++ETypes} | get_tags(Es, Tags, Env, How, File, TypeDocs)];
get_tags([], _, _, _, _, _) ->
[].
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl
index f2e5891c2e..faee8adf7b 100644
--- a/lib/edoc/src/edoc_specs.erl
+++ b/lib/edoc/src/edoc_specs.erl
@@ -42,14 +42,15 @@
%% TypeDocs is a dict of {Name, Doc}.
%% Note: #t_typedef.name is set to {record, R} for record types.
type(Form, TypeDocs) ->
- {Name, Data0} = erl_syntax_lib:analyze_wild_attribute(Form),
- type = tag(Name),
+ {Name, Data0} = analyze_type_attribute(Form),
{TypeName, Type, Args, Doc} =
case Data0 of
- {{record, R}, Fs, []} ->
+ {R, Fs} ->
+ record = Name,
L = erl_syntax:get_pos(Form),
{{record, R}, {type, L, record, [{atom,L,R} | Fs]}, [], ""};
{N,T,As} ->
+ type = tag(Name),
Doc0 =
case dict:find({N, length(As)}, TypeDocs) of
{ok, Doc1} ->
@@ -188,7 +189,7 @@ strip([_ | S]) ->
%% Find the type name and the greatest line number of a type spec.
%% Should use syntax_tools but this has to do for now.
get_name_and_last_line(F) ->
- {Name, Data} = erl_syntax_lib:analyze_wild_attribute(F),
+ {Name, Data} = analyze_type_attribute(F),
type = edoc_specs:tag(Name),
Attr = {attribute, erl_syntax:get_pos(F), Name, Data},
Fun = fun(A) ->
@@ -229,6 +230,7 @@ get_all_tags(Es) ->
%% Turns an opaque type into an abstract datatype.
%% Note: top level annotation is ignored.
opaque2abstr(opaque, _T) -> undefined;
+opaque2abstr(record, T) -> T;
opaque2abstr(type, T) -> T.
%% Replaces the parameters extracted from the source (by
@@ -608,6 +610,16 @@ type_name(#tag{name = type,
data = {#t_typedef{name = Name, args = As},_}}) ->
{Name, length(As)}.
+analyze_type_attribute(Form) ->
+ Name = erl_syntax:atom_value(erl_syntax:attribute_name(Form)),
+ case tag(Name) of
+ type ->
+ erl_syntax_lib:analyze_wild_attribute(Form);
+ _ when Name =:= record ->
+ {attribute, _, record, {N, Fields}} = erl_syntax:revert(Form),
+ {record, {N, Fields}}
+ end.
+
%% @doc Return `true' if `Tag' is one of the specification and type
%% attribute tags recognized by the Erlang compiler.
diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk
index 99c474d588..721387d97d 100644
--- a/lib/eldap/vsn.mk
+++ b/lib/eldap/vsn.mk
@@ -1 +1 @@
-ELDAP_VSN = 1.2.1
+ELDAP_VSN = 1.2.2
diff --git a/lib/et/src/et.app.src b/lib/et/src/et.app.src
index 77b9488c7e..7a5928d6ab 100644
--- a/lib/et/src/et.app.src
+++ b/lib/et/src/et.app.src
@@ -33,6 +33,6 @@
{registered, [et_collector]},
{applications, [stdlib, kernel]},
{env, []},
- {runtime_dependencies, ["wx-1.2","stdlib-2.0","runtime_tools-1.8.14",
- "kernel-3.0","erts-6.0"]}
+ {runtime_dependencies, ["wx-1.2","stdlib-2.0","runtime_tools-1.10",
+ "kernel-3.0","erts-8.0"]}
]}.
diff --git a/lib/et/src/et_selector.erl b/lib/et/src/et_selector.erl
index 0e2d7010cd..a0297c21d1 100644
--- a/lib/et/src/et_selector.erl
+++ b/lib/et/src/et_selector.erl
@@ -23,11 +23,9 @@
-module(et_selector).
--export([
- make_pattern/1,
+-export([make_pattern/1,
change_pattern/1,
- parse_event/2
- ]).
+ parse_event/2]).
-compile([{nowarn_deprecated_function,[{erlang,now,0}]}]).
@@ -535,7 +533,7 @@ parse_event(Mod, Trace, ParsedTS, ReportedTS, From, Label, Contents) ->
{from, From},
{to, From},
{mfa, MFA}]}}; % MFA | 0
- gc_start ->
+ gc_minor_start ->
DetailLevel = 80,
[GcKeyValueList] = Contents,
{true, #event{detail_level = DetailLevel,
@@ -549,7 +547,7 @@ parse_event(Mod, Trace, ParsedTS, ReportedTS, From, Label, Contents) ->
{from, From},
{to, From},
{gc_items, GcKeyValueList}]}};
- gc_end ->
+ gc_minor_end ->
DetailLevel = 85,
[GcKeyValueList] = Contents,
{true, #event{detail_level = DetailLevel,
@@ -559,10 +557,38 @@ parse_event(Mod, Trace, ParsedTS, ReportedTS, From, Label, Contents) ->
to = From,
label = Label,
contents = [{label, Label},
- {detail_level, DetailLevel},
- {from, From},
- {to, From},
- {gc_items, GcKeyValueList}]}};
+ {detail_level, DetailLevel},
+ {from, From},
+ {to, From},
+ {gc_items, GcKeyValueList}]}};
+ gc_major_start ->
+ DetailLevel = 80,
+ [GcKeyValueList] = Contents,
+ {true, #event{detail_level = DetailLevel,
+ trace_ts = ReportedTS,
+ event_ts = ParsedTS,
+ from = From,
+ to = From,
+ label = Label,
+ contents = [{label, Label},
+ {detail_level, DetailLevel},
+ {from, From},
+ {to, From},
+ {gc_items, GcKeyValueList}]}};
+ gc_major_end ->
+ DetailLevel = 85,
+ [GcKeyValueList] = Contents,
+ {true, #event{detail_level = DetailLevel,
+ trace_ts = ReportedTS,
+ event_ts = ParsedTS,
+ from = From,
+ to = From,
+ label = Label,
+ contents = [{label, Label},
+ {detail_level, DetailLevel},
+ {from, From},
+ {to, From},
+ {gc_items, GcKeyValueList}]}};
_ ->
error_logger:format("~p(~p): Ignoring unknown trace type -> ~p~n~n",
[?MODULE, ?LINE, Trace]),
diff --git a/lib/et/test/et_SUITE.erl b/lib/et/test/et_SUITE.erl
index 4a26b46439..199aff43a8 100644
--- a/lib/et/test/et_SUITE.erl
+++ b/lib/et/test/et_SUITE.erl
@@ -16,7 +16,8 @@
%%
-module(et_SUITE).
--compile([export_all]).
+-export([suite/0, all/0]).
+-export([app/1, appup/1]).
-include_lib("common_test/include/ct.hrl").
suite() ->
@@ -25,27 +26,10 @@ suite() ->
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"}].
+%% 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"}].
+%% Test that the et appup file is ok
appup(Config) when is_list(Config) ->
ok = ?t:appup_test(et).
diff --git a/lib/et/test/et_wx_SUITE.erl b/lib/et/test/et_wx_SUITE.erl
index 785d1b8145..6475b6706b 100644
--- a/lib/et/test/et_wx_SUITE.erl
+++ b/lib/et/test/et_wx_SUITE.erl
@@ -19,11 +19,10 @@
-module(et_wx_SUITE).
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
- init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, end_per_testcase/2]).
-
--compile(export_all).
+-export([all/0, suite/0,
+ init_per_testcase/2, end_per_testcase/2,
+ init_per_suite/1, end_per_suite/1]).
+-export([start_all_windows/1]).
-include("et_test_lib.hrl").
@@ -40,21 +39,13 @@ end_per_testcase(Func,Config) ->
et_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-suite() -> [{ct_hooks,[ts_install_cth]}].
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
all() ->
[start_all_windows].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%% The test cases
%% Display all windows and see if something crashes
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index 4dbe023257..9dbb4835f8 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -192,7 +192,6 @@ error_msg(Title, Fmt, Args) ->
io_lib:fwrite("*** ~ts ***\n~ts\n\n", [Title, Msg]).
-ifdef(TEST).
--dialyzer({no_match, format_exception_test_/0}).
format_exception_test_() ->
[?_assertMatch(
"\nymmud:rorre"++_,
@@ -274,7 +273,6 @@ dlist_next([], Xs) ->
-ifdef(TEST).
--dialyzer({no_match, dlist_test_/0}).
dlist_test_() ->
{"deep list traversal",
[{"non-list term -> singleton list",
@@ -340,7 +338,6 @@ is_nonempty_string([]) -> false;
is_nonempty_string(Cs) -> is_string(Cs).
-ifdef(TEST).
--dialyzer({no_match, is_string_test_/0}).
is_string_test_() ->
{"is_string",
[{"no non-lists", ?_assert(not is_string($A))},
@@ -402,7 +399,7 @@ uniq([X | Xs]) -> [X | uniq(Xs)];
uniq([]) -> [].
-ifdef(TEST).
--dialyzer({[no_match, no_fail_call, no_improper_lists], uniq_test_/0}).
+-dialyzer({[no_fail_call, no_improper_lists], uniq_test_/0}).
uniq_test_() ->
{"uniq",
[?_assertError(function_clause, uniq(ok)),
@@ -581,7 +578,6 @@ trie_match([], _T) ->
-ifdef(TEST).
--dialyzer({no_match, trie_test_/0}).
trie_test_() ->
[{"basic representation",
[?_assert(trie_new() =:= gb_trees:empty()),
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 7684dc4a81..9453ca6c6f 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -115,7 +115,16 @@
t_tuple_size/2,
t_tuple_subtypes/2,
t_is_map/2,
- t_map/0
+ t_map/0,
+ t_map/3,
+ t_map_def_key/2,
+ t_map_def_val/2,
+ t_map_get/3,
+ t_map_is_key/3,
+ t_map_entries/2,
+ t_map_put/3,
+ t_map_update/3,
+ map_pairwise_merge/3
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -755,7 +764,7 @@ type(erlang, length, 1, Xs, Opaques) ->
strict(erlang, length, 1, Xs, fun (_) -> t_non_neg_fixnum() end, Opaques);
%% Guard bif, needs to be here.
type(erlang, map_size, 1, Xs, Opaques) ->
- strict(erlang, map_size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques);
+ type(maps, size, 1, Xs, Opaques);
type(erlang, make_fun, 3, Xs, Opaques) ->
strict(erlang, make_fun, 3, Xs,
fun ([_, _, Arity]) ->
@@ -1645,6 +1654,89 @@ type(lists, zipwith3, 4, Xs, Opaques) ->
fun ([F,_As,_Bs,_Cs]) -> t_sup(t_list(t_fun_range(F, Opaques)),
t_nil()) end, Opaques);
+%%-- maps ---------------------------------------------------------------------
+type(maps, from_list, 1, Xs, Opaques) ->
+ strict(maps, from_list, 1, Xs,
+ fun ([List]) ->
+ case t_is_nil(List, Opaques) of
+ true -> t_from_term(#{});
+ false ->
+ T = t_list_elements(List, Opaques),
+ case t_tuple_subtypes(T, Opaques) of
+ unknown -> t_map();
+ Stypes when length(Stypes) >= 1 ->
+ t_sup([begin
+ [K, V] = t_tuple_args(Args, Opaques),
+ t_map([], K, V)
+ end || Args <- Stypes])
+ end
+ end
+ end, Opaques);
+type(maps, get, 2, Xs, Opaques) ->
+ strict(maps, get, 2, Xs,
+ fun ([Key, Map]) ->
+ t_map_get(Key, Map, Opaques)
+ end, Opaques);
+type(maps, is_key, 2, Xs, Opaques) ->
+ strict(maps, is_key, 2, Xs,
+ fun ([Key, Map]) ->
+ t_map_is_key(Key, Map, Opaques)
+ end, Opaques);
+type(maps, merge, 2, Xs, Opaques) ->
+ strict(maps, merge, 2, Xs,
+ fun ([MapA, MapB]) ->
+ ADefK = t_map_def_key(MapA, Opaques),
+ BDefK = t_map_def_key(MapB, Opaques),
+ ADefV = t_map_def_val(MapA, Opaques),
+ BDefV = t_map_def_val(MapB, Opaques),
+ t_map(map_pairwise_merge(
+ fun(K, _, _, mandatory, V) -> {K, mandatory, V};
+ (K, MNess, VA, optional, VB) -> {K, MNess, t_sup(VA,VB)}
+ end, MapA, MapB),
+ t_sup(ADefK, BDefK), t_sup(ADefV, BDefV))
+ end, Opaques);
+type(maps, put, 3, Xs, Opaques) ->
+ strict(maps, put, 3, Xs,
+ fun ([Key, Value, Map]) ->
+ t_map_put({Key, Value}, Map, Opaques)
+ end, Opaques);
+type(maps, size, 1, Xs, Opaques) ->
+ strict(maps, size, 1, Xs,
+ fun ([Map]) ->
+ Mand = [E || E={_,mandatory,_} <- t_map_entries(Map, Opaques)],
+ LowerBound = length(Mand),
+ case t_is_none(t_map_def_key(Map, Opaques)) of
+ false -> t_from_range(LowerBound, pos_inf);
+ true ->
+ Opt = [E || E={_,optional,_} <- t_map_entries(Map, Opaques)],
+ UpperBound = LowerBound + length(Opt),
+ t_from_range(LowerBound, UpperBound)
+ end
+ end, Opaques);
+type(maps, to_list, 1, Xs, Opaques) ->
+ strict(maps, to_list, 1, Xs,
+ fun ([Map]) ->
+ DefK = t_map_def_key(Map, Opaques),
+ DefV = t_map_def_val(Map, Opaques),
+ Pairs = t_map_entries(Map, Opaques),
+ EType = lists:foldl(
+ fun({K,_,V},EType0) ->
+ case t_is_none(V) of
+ true -> t_subtract(EType0, t_tuple([K,t_any()]));
+ false -> t_sup(EType0, t_tuple([K,V]))
+ end
+ end, t_tuple([DefK, DefV]), Pairs),
+ case t_is_none(EType) of
+ true -> t_nil();
+ false -> t_list(EType)
+ end
+ end, Opaques);
+type(maps, update, 3, Xs, Opaques) ->
+ strict(maps, update, 3, Xs,
+ fun ([Key, Value, Map]) ->
+ t_map_update({Key, Value}, Map, Opaques)
+ end, Opaques);
+
%%-----------------------------------------------------------------------------
type(M, F, A, Xs, _O) when is_atom(M), is_atom(F),
is_integer(A), 0 =< A, A =< 255 ->
@@ -2556,6 +2648,23 @@ arg_types(lists, zipwith, 3) ->
[t_fun([t_any(), t_any()], t_any()), t_list(), t_list()];
arg_types(lists, zipwith3, 4) ->
[t_fun([t_any(), t_any(), t_any()], t_any()), t_list(), t_list(), t_list()];
+%%------- maps ----------------------------------------------------------------
+arg_types(maps, from_list, 1) ->
+ [t_list(t_tuple(2))];
+arg_types(maps, get, 2) ->
+ [t_any(), t_map()];
+arg_types(maps, is_key, 2) ->
+ [t_any(), t_map()];
+arg_types(maps, merge, 2) ->
+ [t_map(), t_map()];
+arg_types(maps, put, 3) ->
+ [t_any(), t_any(), t_map()];
+arg_types(maps, size, 1) ->
+ [t_map()];
+arg_types(maps, to_list, 1) ->
+ [t_map()];
+arg_types(maps, update, 3) ->
+ [t_any(), t_any(), t_map()];
arg_types(M, F, A) when is_atom(M), is_atom(F),
is_integer(A), 0 =< A, A =< 255 ->
unknown. % safe approximation for all functions.
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index fae12d7421..84addcc105 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -140,6 +140,8 @@
t_is_port/1, t_is_port/2,
t_is_maybe_improper_list/1, t_is_maybe_improper_list/2,
t_is_reference/1, t_is_reference/2,
+ t_is_singleton/1,
+ t_is_singleton/2,
t_is_string/1,
t_is_subtype/2,
t_is_tuple/1, t_is_tuple/2,
@@ -152,6 +154,14 @@
t_list_termination/1, t_list_termination/2,
t_map/0,
t_map/1,
+ t_map/3,
+ t_map_entries/2, t_map_entries/1,
+ t_map_def_key/2, t_map_def_key/1,
+ t_map_def_val/2, t_map_def_val/1,
+ t_map_get/2, t_map_get/3,
+ t_map_is_key/2, t_map_is_key/3,
+ t_map_update/2, t_map_update/3,
+ t_map_put/2, t_map_put/3,
t_matchstate/0,
t_matchstate/2,
t_matchstate_present/1,
@@ -178,6 +188,7 @@
%% t_maybe_improper_list/2,
t_product/1,
t_reference/0,
+ t_singleton_to_term/2,
t_string/0,
t_struct_from_opaque/2,
t_subst/2,
@@ -208,7 +219,8 @@
lift_list_to_pos_empty/1, lift_list_to_pos_empty/2,
is_opaque_type/2,
is_erl_type/1,
- atom_to_string/1
+ atom_to_string/1,
+ map_pairwise_merge/3
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -224,7 +236,7 @@
-export([t_is_identifier/1]).
-endif.
--export_type([erl_type/0, opaques/0, type_table/0, var_table/0]).
+-export_type([erl_type/0, opaques/0, type_table/0]).
%%-define(DEBUG, true).
@@ -341,7 +353,8 @@
-define(nonempty_list(Types, Term),?list(Types, Term, ?nonempty_qual)).
-define(number(Set, Qualifier), #c{tag=?number_tag, elements=Set,
qualifier=Qualifier}).
--define(map(Pairs), #c{tag=?map_tag, elements=Pairs}).
+-define(map(Pairs,DefKey,DefVal),
+ #c{tag=?map_tag, elements={Pairs,DefKey,DefVal}}).
-define(opaque(Optypes), #c{tag=?opaque_tag, elements=Optypes}).
-define(product(Types), #c{tag=?product_tag, elements=Types}).
-define(tuple(Types, Arity, Qual), #c{tag=?tuple_tag, elements=Types,
@@ -367,7 +380,7 @@
-type type_table() :: dict:dict(record_key() | type_key(),
record_value() | type_value()).
--type var_table() :: dict:dict(atom(), erl_type()).
+-type var_table() :: #{atom() => erl_type()}.
%%-----------------------------------------------------------------------------
%% Unions
@@ -484,9 +497,8 @@ 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(_) = Map, Opaques) ->
- list_contains_opaque(map_values(Map), Opaques) orelse
- list_contains_opaque(map_keys(Map), Opaques);
+t_contains_opaque(?map(_, _, _) = Map, Opaques) ->
+ list_contains_opaque(map_all_types(Map), Opaques);
t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false;
t_contains_opaque(?nil, _Opaques) -> false;
t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false;
@@ -1581,16 +1593,107 @@ lift_list_to_pos_empty(?list(Content, Termination, _)) ->
%%-----------------------------------------------------------------------------
%% Maps
%%
+%% Representation:
+%% ?map(Pairs, DefaultKey, DefaultValue)
+%%
+%% Pairs is a sorted dictionary of types with a mandatoriness tag on each pair
+%% (t_map_dict()). DefaultKey and DefaultValue are plain types.
+%%
+%% A map M belongs to this type iff
+%% For each pair {KT, mandatory, VT} in Pairs, there exists a pair {K, V} in M
+%% such that K \in KT and V \in VT.
+%% For each pair {KT, optional, VT} in Pairs, either there exists no key K in
+%% M s.t. K in KT, or there exists a pair {K, V} in M such that K \in KT and
+%% V \in VT.
+%% For each remaining pair {K, V} in M (where remaining means that there is no
+%% key KT in Pairs s.t. K \in KT), K \in DefaultKey and V \in DefaultValue.
+%%
+%% Invariants:
+%% * The keys in Pairs are singleton types.
+%% * The values of Pairs must not be unit, and may only be none if the
+%% mandatoriness tag is 'optional'.
+%% * Optional must contain no pair {K,V} s.t. K is a subtype of DefaultKey and
+%% V is equal to DefaultKey.
+%% * DefaultKey must be the empty type iff DefaultValue is the empty type.
+%% * DefaultKey must not be a singleton type.
+%% * For every key K in Pairs, DefaultKey - K must not be representable; i.e.
+%% t_subtract(DefaultKey, K) must return DefaultKey.
+%% * For every pair {K, 'optional', ?none} in Pairs, K must be a subtype of
+%% DefaultKey.
+%% * Pairs must be sorted and not contain any duplicate keys.
+%%
+%% These invariants ensure that equal map types are represented by equal terms.
+
+-define(mand, mandatory).
+-define(opt, optional).
+
+-type t_map_mandatoriness() :: ?mand | ?opt.
+-type t_map_pair() :: {erl_type(), t_map_mandatoriness(), erl_type()}.
+-type t_map_dict() :: [t_map_pair()].
-spec t_map() -> erl_type().
t_map() ->
- ?map([]).
+ t_map([], t_any(), t_any()).
-spec t_map([{erl_type(), erl_type()}]) -> erl_type().
-t_map(_) ->
- ?map([]).
+t_map(L) ->
+ lists:foldl(fun t_map_put/2, t_map(), L).
+
+-spec t_map(t_map_dict(), erl_type(), erl_type()) -> erl_type().
+
+t_map(Pairs0, DefK0, DefV0) ->
+ DefK1 = lists:foldl(fun({K,_,_},Acc)->t_subtract(Acc,K)end, DefK0, Pairs0),
+ {DefK2, DefV1} =
+ case t_is_none_or_unit(DefK1) orelse t_is_none_or_unit(DefV0) of
+ true -> {?none, ?none};
+ false -> {DefK1, DefV0}
+ end,
+ {Pairs1, DefK, DefV}
+ = case is_singleton_type(DefK2) of
+ true -> {mapdict_insert({DefK2, ?opt, DefV1}, Pairs0), ?none, ?none};
+ false -> {Pairs0, DefK2, DefV1}
+ end,
+ Pairs = normalise_map_optionals(Pairs1, DefK, DefV),
+ %% Validate invariants of the map representation.
+ %% Since we needed to iterate over the arguments in order to normalise anyway,
+ %% we might as well save us some future pain and do this even without
+ %% define(DEBUG, true).
+ try
+ validate_map_elements(Pairs)
+ catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]);
+ error:{badarg, E} -> error({badarg, E}, [Pairs0,DefK0,DefV0])
+ end,
+ ?map(Pairs, DefK, DefV).
+
+normalise_map_optionals([], _, _) -> [];
+normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) ->
+ Diff = t_subtract(DefK, K),
+ case t_is_subtype(K, DefK) andalso DefK =:= Diff of
+ true -> [E|normalise_map_optionals(T, DefK, DefV)];
+ false -> normalise_map_optionals(T, Diff, DefV)
+ end;
+normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV) ->
+ case t_is_equal(V, DefV) andalso t_is_subtype(K, DefK) of
+ true -> normalise_map_optionals(T, DefK, DefV);
+ false -> [E|normalise_map_optionals(T, DefK, DefV)]
+ end;
+normalise_map_optionals([E|T], DefK, DefV) ->
+ [E|normalise_map_optionals(T, DefK, DefV)].
+
+validate_map_elements([{_,?mand,?none}|_]) -> error({badarg, none_in_mand});
+validate_map_elements([{K1,_,_}|Rest=[{K2,_,_}|_]]) ->
+ case is_singleton_type(K1) andalso K1 < K2 of
+ false -> error(badarg);
+ true -> validate_map_elements(Rest)
+ end;
+validate_map_elements([{K,_,_}]) ->
+ case is_singleton_type(K) of
+ false -> error(badarg);
+ true -> true
+ end;
+validate_map_elements([]) -> true.
-spec t_is_map(erl_type()) -> boolean().
@@ -1602,9 +1705,242 @@ t_is_map(Type) ->
t_is_map(Type, Opaques) ->
do_opaque(Type, Opaques, fun is_map1/1).
-is_map1(?map(_)) -> true;
+is_map1(?map(_, _, _)) -> true;
is_map1(_) -> false.
+-spec t_map_entries(erl_type()) -> t_map_dict().
+
+t_map_entries(M) ->
+ t_map_entries(M, 'universe').
+
+-spec t_map_entries(erl_type(), opaques()) -> t_map_dict().
+
+t_map_entries(M, Opaques) ->
+ do_opaque(M, Opaques, fun map_entries/1).
+
+map_entries(?map(Pairs,_,_)) ->
+ Pairs.
+
+-spec t_map_def_key(erl_type()) -> erl_type().
+
+t_map_def_key(M) ->
+ t_map_def_key(M, 'universe').
+
+-spec t_map_def_key(erl_type(), opaques()) -> erl_type().
+
+t_map_def_key(M, Opaques) ->
+ do_opaque(M, Opaques, fun map_def_key/1).
+
+map_def_key(?map(_,DefK,_)) ->
+ DefK.
+
+-spec t_map_def_val(erl_type()) -> erl_type().
+
+t_map_def_val(M) ->
+ t_map_def_val(M, 'universe').
+
+-spec t_map_def_val(erl_type(), opaques()) -> erl_type().
+
+t_map_def_val(M, Opaques) ->
+ do_opaque(M, Opaques, fun map_def_val/1).
+
+map_def_val(?map(_,_,DefV)) ->
+ DefV.
+
+-spec mapdict_store(t_map_pair(), t_map_dict()) -> t_map_dict().
+
+mapdict_store(E={K,_,_}, [{K,_,_}|T]) -> [E|T];
+mapdict_store(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2->
+ [E2|mapdict_store(E1, T)];
+mapdict_store(E={_,_,_}, T) -> [E|T].
+
+-spec mapdict_insert(t_map_pair(), t_map_dict()) -> t_map_dict().
+
+mapdict_insert(E={K,_,_}, D=[{K,_,_}|_]) -> error(badarg, [E, D]);
+mapdict_insert(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2->
+ [E2|mapdict_insert(E1, T)];
+mapdict_insert(E={_,_,_}, T) -> [E|T].
+
+%% Merges the pairs of two maps together. Missing pairs become (?opt, DefV) or
+%% (?opt, ?none), depending on whether K \in DefK.
+-spec map_pairwise_merge(fun((erl_type(),
+ t_map_mandatoriness(), erl_type(),
+ t_map_mandatoriness(), erl_type())
+ -> t_map_pair() | false),
+ erl_type(), erl_type()) -> t_map_dict().
+map_pairwise_merge(F, ?map(APairs, ADefK, ADefV),
+ ?map(BPairs, BDefK, BDefV)) ->
+ map_pairwise_merge(F, APairs, ADefK, ADefV, BPairs, BDefK, BDefV).
+
+map_pairwise_merge(_, [], _, _, [], _, _) -> [];
+map_pairwise_merge(F, As0, ADefK, ADefV, Bs0, BDefK, BDefV) ->
+ case {As0, Bs0} of
+ {[{K,AMNess,AV}|As], [{K, BMNess,BV}|Bs]} -> ok;
+ {[{K,AMNess,AV}|As], [{BK,_, _ }|_]=Bs} when K < BK ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)};
+ {As, [{K, BMNess,BV}|Bs]} ->
+ {AMNess, AV} = {?opt, mapmerge_otherv(K, ADefK, ADefV)};
+ {[{K,AMNess,AV}|As], []=Bs} ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)}
+ end,
+ MK = K, %% Rename to make clear that we are matching below
+ case F(K, AMNess, AV, BMNess, BV) of
+ false -> map_pairwise_merge(F,As,ADefK,ADefV,Bs,BDefK,BDefV);
+ M={MK,_,_} -> [M|map_pairwise_merge(F,As,ADefK,ADefV,Bs,BDefK,BDefV)]
+ end.
+
+%% Folds over the pairs in two maps simultaneously in reverse key order. Missing
+%% pairs become (?opt, DefV) or (?opt, ?none), depending on whether K \in DefK.
+-spec map_pairwise_merge_foldr(fun((erl_type(),
+ t_map_mandatoriness(), erl_type(),
+ t_map_mandatoriness(), erl_type(),
+ Acc) -> Acc),
+ Acc, erl_type(), erl_type()) -> Acc.
+
+map_pairwise_merge_foldr(F, AccIn, ?map(APairs, ADefK, ADefV),
+ ?map(BPairs, BDefK, BDefV)) ->
+ map_pairwise_merge_foldr(F, AccIn, APairs, ADefK, ADefV, BPairs, BDefK, BDefV).
+
+map_pairwise_merge_foldr(_, Acc, [], _, _, [], _, _) -> Acc;
+map_pairwise_merge_foldr(F, AccIn, As0, ADefK, ADefV, Bs0, BDefK, BDefV) ->
+ case {As0, Bs0} of
+ {[{K,AMNess,AV}|As], [{K, BMNess,BV}|Bs]} -> ok;
+ {[{K,AMNess,AV}|As], [{BK,_, _ }|_]=Bs} when K < BK ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)};
+ {As, [{K, BMNess,BV}|Bs]} ->
+ {AMNess, AV} = {?opt, mapmerge_otherv(K, ADefK, ADefV)};
+ {[{K,AMNess,AV}|As], []=Bs} ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)}
+ end,
+ F(K, AMNess, AV, BMNess, BV,
+ map_pairwise_merge_foldr(F,AccIn,As,ADefK,ADefV,Bs,BDefK,BDefV)).
+
+%% By observing that a missing pair in a map is equivalent to an optional pair,
+%% with ?none or DefV value, depending on whether K \in DefK, we can simplify
+%% merging by denormalising the map pairs temporarily, removing all 'false'
+%% cases, at the cost of the creation of more tuples:
+mapmerge_otherv(K, ODefK, ODefV) ->
+ case t_inf(K, ODefK) of
+ ?none -> ?none;
+ _KOrOpaque -> ODefV
+ end.
+
+-spec t_map_put({erl_type(), erl_type()}, erl_type()) -> erl_type().
+
+t_map_put(KV, Map) ->
+ t_map_put(KV, Map, 'universe').
+
+-spec t_map_put({erl_type(), erl_type()}, erl_type(), opaques()) -> erl_type().
+
+t_map_put(KV, Map, Opaques) ->
+ do_opaque(Map, Opaques, fun(UM) -> map_put(KV, UM, Opaques) end).
+
+%% Key and Value are *not* unopaqued, but the map is
+map_put(_, ?none, _) -> ?none;
+map_put({Key, Value}, ?map(Pairs,DefK,DefV), Opaques) ->
+ case t_is_none_or_unit(Key) orelse t_is_none_or_unit(Value) of
+ true -> ?none;
+ false ->
+ case is_singleton_type(Key) of
+ true ->
+ t_map(mapdict_store({Key, ?mand, Value}, Pairs), DefK, DefV);
+ false ->
+ t_map([{K, MNess, case t_is_none(t_inf(K, Key, Opaques)) of
+ true -> V;
+ false -> t_sup(V, Value)
+ end} || {K, MNess, V} <- Pairs],
+ t_sup(DefK, Key),
+ t_sup(DefV, Value))
+ end
+ end.
+
+-spec t_map_update({erl_type(), erl_type()}, erl_type()) -> erl_type().
+
+t_map_update(KV, Map) ->
+ t_map_update(KV, Map, 'universe').
+
+-spec t_map_update({erl_type(), erl_type()}, erl_type(), opaques()) -> erl_type().
+
+t_map_update(_, ?none, _) -> ?none;
+t_map_update(KV={Key, _}, M, Opaques) ->
+ case t_is_subtype(t_atom('true'), t_map_is_key(Key, M, Opaques)) of
+ false -> ?none;
+ true -> t_map_put(KV, M, Opaques)
+ end.
+
+-spec t_map_get(erl_type(), erl_type()) -> erl_type().
+
+t_map_get(Key, Map) ->
+ t_map_get(Key, Map, 'universe').
+
+-spec t_map_get(erl_type(), erl_type(), opaques()) -> erl_type().
+
+t_map_get(Key, Map, Opaques) ->
+ do_opaque(Map, Opaques,
+ fun(UM) ->
+ do_opaque(Key, Opaques, fun(UK) -> map_get(UK, UM) end)
+ end).
+
+map_get(_, ?none) -> ?none;
+map_get(Key, ?map(Pairs, DefK, DefV)) ->
+ DefRes =
+ case t_do_overlap(DefK, Key) of
+ false -> t_none();
+ true -> DefV
+ end,
+ case is_singleton_type(Key) of
+ false ->
+ lists:foldl(fun({K, _, V}, Res) ->
+ case t_do_overlap(K, Key) of
+ false -> Res;
+ true -> t_sup(Res, V)
+ end
+ end, DefRes, Pairs);
+ true ->
+ case lists:keyfind(Key, 1, Pairs) of
+ false -> DefRes;
+ {_, _, ValType} -> ValType
+ end
+ end.
+
+-spec t_map_is_key(erl_type(), erl_type()) -> erl_type().
+
+t_map_is_key(Key, Map) ->
+ t_map_is_key(Key, Map, 'universe').
+
+-spec t_map_is_key(erl_type(), erl_type(), opaques()) -> erl_type().
+
+t_map_is_key(Key, Map, Opaques) ->
+ do_opaque(Map, Opaques,
+ fun(UM) ->
+ do_opaque(Key, Opaques, fun(UK) -> map_is_key(UK, UM) end)
+ end).
+
+map_is_key(_, ?none) -> ?none;
+map_is_key(Key, ?map(Pairs, DefK, _DefV)) ->
+ case is_singleton_type(Key) of
+ true ->
+ case lists:keyfind(Key, 1, Pairs) of
+ {Key, ?mand, _} -> t_atom(true);
+ {Key, ?opt, ?none} -> t_atom(false);
+ {Key, ?opt, _} -> t_boolean();
+ false ->
+ case t_do_overlap(DefK, Key) of
+ false -> t_atom(false);
+ true -> t_boolean()
+ end
+ end;
+ false ->
+ case t_do_overlap(DefK, Key)
+ orelse lists:any(fun({_,_,?none}) -> false;
+ ({K,_,_}) -> t_do_overlap(K, Key)
+ end, Pairs)
+ of
+ true -> t_boolean();
+ false -> t_atom(false)
+ end
+ end.
+
%%-----------------------------------------------------------------------------
%% Tuples
%%
@@ -1862,8 +2198,9 @@ 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(?map(_)= Map) ->
- t_has_var_list(map_keys(Map)) orelse t_has_var_list(map_values(Map));
+t_has_var(?map(_, DefK, _)= Map) ->
+ t_has_var_list(map_all_values(Map)) orelse
+ t_has_var(DefK);
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)]);
@@ -1898,9 +2235,9 @@ t_collect_vars(?tuple(Types, _, _), Acc) ->
t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple_set(_) = TS, 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(?map(_, DefK, _) = Map, Acc0) ->
+ Acc = t_collect_vars_list(map_all_values(Map), Acc0),
+ t_collect_vars(DefK, 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);
@@ -1935,7 +2272,15 @@ 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_map(T) ->
+ Pairs = [{t_from_term(K), ?mand, t_from_term(V)}
+ || {K, V} <- maps:to_list(T)],
+ {Stons, Rest} = lists:partition(fun({K,_,_}) -> is_singleton_type(K) end,
+ Pairs),
+ {DefK, DefV}
+ = lists:foldl(fun({K,_,V},{AK,AV}) -> {t_sup(K,AK), t_sup(V,AV)} end,
+ {t_none(), t_none()}, Rest),
+ t_map(lists:keysort(1, Stons), DefK, DefV);
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();
@@ -2225,6 +2570,13 @@ t_sup(?tuple_set(List1), T2 = ?tuple(_, Arity, _)) ->
sup_tuple_sets(List1, [{Arity, [T2]}]);
t_sup(?tuple(_, Arity, _) = T1, ?tuple_set(List2)) ->
sup_tuple_sets([{Arity, [T1]}], List2);
+t_sup(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B) ->
+ Pairs =
+ map_pairwise_merge(
+ fun(K, MNess, V1, MNess, V2) -> {K, MNess, t_sup(V1, V2)};
+ (K, _, V1, _, V2) -> {K, ?opt, t_sup(V1, V2)}
+ end, A, B),
+ t_map(Pairs, t_sup(ADefK, BDefK), t_sup(ADefV, BDefV));
t_sup(T1, T2) ->
?union(U1) = force_union(T1),
?union(U2) = force_union(T2),
@@ -2343,7 +2695,7 @@ force_union(T = ?list(_, _, _)) -> ?list_union(T);
force_union(T = ?nil) -> ?list_union(T);
force_union(T = ?number(_, _)) -> ?number_union(T);
force_union(T = ?opaque(_)) -> ?opaque_union(T);
-force_union(T = ?map(_)) -> ?map_union(T);
+force_union(T = ?map(_,_,_)) -> ?map_union(T);
force_union(T = ?tuple(_, _, _)) -> ?tuple_union(T);
force_union(T = ?tuple_set(_)) -> ?tuple_union(T);
force_union(T = ?matchstate(_, _)) -> ?matchstate_union(T);
@@ -2380,7 +2732,7 @@ t_elements(?number(_, _) = T) ->
end;
t_elements(?opaque(_) = T) ->
do_elements(T);
-t_elements(?map(_) = T) -> [T];
+t_elements(?map(_,_,_) = T) -> [T];
t_elements(?tuple(_, _, _) = T) -> [T];
t_elements(?tuple_set(_) = TS) ->
case t_tuple_subtypes(TS) of
@@ -2462,6 +2814,25 @@ t_inf(?identifier(Set1), ?identifier(Set2), _Opaques) ->
?none -> ?none;
Set -> ?identifier(Set)
end;
+t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) ->
+ %% Because it simplifies the anonymous function, we allow Pairs to temporarily
+ %% contain mandatory pairs with none values, since all such cases should
+ %% result in a none result.
+ Pairs =
+ map_pairwise_merge(
+ %% For optional keys in both maps, when the infinimum is none, we have
+ %% essentially concluded that K must not be a key in the map.
+ fun(K, ?opt, V1, ?opt, V2) -> {K, ?opt, t_inf(V1, V2)};
+ %% When a key is optional in one map, but mandatory in another, it
+ %% becomes mandatory in the infinumum
+ (K, _, V1, _, V2) -> {K, ?mand, t_inf(V1, V2)}
+ end, A, B),
+ %% If the infinimum of any mandatory values is ?none, the entire map infinimum
+ %% is ?none.
+ case lists:any(fun({_,?mand,?none})->true; ({_,_,_}) -> false end, Pairs) of
+ true -> t_none();
+ false -> t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV))
+ end;
t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) ->
?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2));
t_inf(?nil, ?nil, _Opaques) -> ?nil;
@@ -2924,87 +3295,33 @@ findfirst(N1, N2, U1, B1, U2, B2) ->
%%-----------------------------------------------------------------------------
%% Substitution of variables
%%
-%% Dialyzer versions prior to R15B used a dict data structure to map variables
-%% 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: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:dict(atom(), erl_type())) -> erl_type().
-
-t_subst(T, Dict) ->
+
+-type subst_table() :: #{any() => erl_type()}.
+
+-spec t_subst(erl_type(), subst_table()) -> erl_type().
+
+t_subst(T, Map) ->
case t_has_var(T) of
- true -> t_subst_dict(T, Dict);
+ true -> t_subst_aux(T, Map);
false -> T
end.
-t_subst_dict(?var(Id), Dict) ->
- case dict:find(Id, Dict) of
- error -> ?any;
- {ok, Type} -> Type
- end;
-t_subst_dict(?list(Contents, Termination, Size), Dict) ->
- case t_subst_dict(Contents, Dict) of
- ?none -> ?none;
- NewContents ->
- %% Be careful here to make the termination collapse if necessary.
- case t_subst_dict(Termination, Dict) of
- ?nil -> ?list(NewContents, ?nil, Size);
- ?any -> ?list(NewContents, ?any, Size);
- Other ->
- ?list(NewContents2, NewTermination, _) = t_cons(NewContents, Other),
- ?list(NewContents2, NewTermination, Size)
- end
- end;
-t_subst_dict(?function(Domain, Range), Dict) ->
- ?function(t_subst_dict(Domain, Dict), t_subst_dict(Range, Dict));
-t_subst_dict(?product(Types), Dict) ->
- ?product([t_subst_dict(T, Dict) || T <- Types]);
-t_subst_dict(?tuple(?any, ?any, ?any) = T, _Dict) ->
- T;
-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(?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.
-
-spec subst_all_vars_to_any(erl_type()) -> erl_type().
subst_all_vars_to_any(T) ->
- t_subst_kv(T, []).
-
-t_subst_kv(T, KVMap) ->
- case t_has_var(T) of
- true -> t_subst_aux(T, KVMap);
- false -> T
- end.
+ t_subst(T, #{}).
-t_subst_aux(?var(Id), VarMap) ->
- case lists:keyfind(Id, 1, VarMap) of
- false -> ?any;
- {Id, Type} -> Type
+t_subst_aux(?var(Id), Map) ->
+ case maps:find(Id, Map) of
+ error -> ?any;
+ {ok, Type} -> Type
end;
-t_subst_aux(?list(Contents, Termination, Size), VarMap) ->
- case t_subst_aux(Contents, VarMap) of
+t_subst_aux(?list(Contents, Termination, Size), Map) ->
+ case t_subst_aux(Contents, Map) of
?none -> ?none;
NewContents ->
%% Be careful here to make the termination collapse if necessary.
- case t_subst_aux(Termination, VarMap) of
+ case t_subst_aux(Termination, Map) of
?nil -> ?list(NewContents, ?nil, Size);
?any -> ?list(NewContents, ?any, Size);
Other ->
@@ -3012,27 +3329,27 @@ t_subst_aux(?list(Contents, Termination, Size), VarMap) ->
?list(NewContents2, NewTermination, Size)
end
end;
-t_subst_aux(?function(Domain, Range), VarMap) ->
- ?function(t_subst_aux(Domain, VarMap), t_subst_aux(Range, VarMap));
-t_subst_aux(?product(Types), VarMap) ->
- ?product([t_subst_aux(T, VarMap) || T <- Types]);
-t_subst_aux(?tuple(?any, ?any, ?any) = T, _VarMap) ->
+t_subst_aux(?function(Domain, Range), Map) ->
+ ?function(t_subst_aux(Domain, Map), t_subst_aux(Range, Map));
+t_subst_aux(?product(Types), Map) ->
+ ?product([t_subst_aux(T, Map) || T <- Types]);
+t_subst_aux(?tuple(?any, ?any, ?any) = T, _Map) ->
T;
-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(?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_subst_aux(?tuple(Elements, _Arity, _Tag), Map) ->
+ t_tuple([t_subst_aux(E, Map) || E <- Elements]);
+t_subst_aux(?tuple_set(_) = TS, Map) ->
+ t_sup([t_subst_aux(T, Map) || T <- t_tuple_subtypes(TS)]);
+t_subst_aux(?map(Pairs, DefK, DefV), Map) ->
+ t_map([{K, MNess, t_subst_aux(V, Map)} || {K, MNess, V} <- Pairs],
+ t_subst_aux(DefK, Map), t_subst_aux(DefV, Map));
+t_subst_aux(?opaque(Es), Map) ->
+ List = [Opaque#opaque{args = [t_subst_aux(Arg, Map) || Arg <- Args],
+ struct = t_subst_aux(S, Map)} ||
+ Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)],
+ ?opaque(ordsets:from_list(List));
+t_subst_aux(?union(List), Map) ->
+ ?union([t_subst_aux(E, Map) || E <- List]);
+t_subst_aux(T, _Map) ->
T.
%%-----------------------------------------------------------------------------
@@ -3044,33 +3361,33 @@ t_subst_aux(T, _VarMap) ->
-spec t_unify(erl_type(), erl_type()) -> t_unify_ret().
t_unify(T1, T2) ->
- {T, VarMap} = t_unify(T1, T2, []),
- {t_subst_kv(T, VarMap), lists:keysort(1, VarMap)}.
+ {T, VarMap} = t_unify(T1, T2, #{}),
+ {t_subst(T, VarMap), lists:keysort(1, maps:to_list(VarMap))}.
t_unify(?var(Id) = T, ?var(Id), VarMap) ->
{T, VarMap};
t_unify(?var(Id1) = T, ?var(Id2), VarMap) ->
- case lists:keyfind(Id1, 1, VarMap) of
- false ->
- case lists:keyfind(Id2, 1, VarMap) of
- false -> {T, [{Id2, T} | VarMap]};
- {Id2, Type} -> t_unify(T, Type, VarMap)
+ case maps:find(Id1, VarMap) of
+ error ->
+ case maps:find(Id2, VarMap) of
+ error -> {T, VarMap#{Id2 => T}};
+ {ok, Type} -> t_unify(T, Type, VarMap)
end;
- {Id1, Type1} ->
- case lists:keyfind(Id2, 1, VarMap) of
- false -> {Type1, [{Id2, T} | VarMap]};
- {Id2, Type2} -> t_unify(Type1, Type2, VarMap)
+ {ok, Type1} ->
+ case maps:find(Id2, VarMap) of
+ error -> {Type1, VarMap#{Id2 => T}};
+ {ok, Type2} -> t_unify(Type1, Type2, VarMap)
end
end;
t_unify(?var(Id), Type, VarMap) ->
- case lists:keyfind(Id, 1, VarMap) of
- false -> {Type, [{Id, Type} | VarMap]};
- {Id, VarType} -> t_unify(VarType, Type, VarMap)
+ case maps:find(Id, VarMap) of
+ error -> {Type, VarMap#{Id => Type}};
+ {ok, VarType} -> t_unify(VarType, Type, VarMap)
end;
t_unify(Type, ?var(Id), VarMap) ->
- case lists:keyfind(Id, 1, VarMap) of
- false -> {Type, [{Id, Type} | VarMap]};
- {Id, VarType} -> t_unify(VarType, Type, VarMap)
+ case maps:find(Id, VarMap) of
+ error -> {Type, VarMap#{Id => Type}};
+ {ok, VarType} -> t_unify(VarType, Type, VarMap)
end;
t_unify(?function(Domain1, Range1), ?function(Domain2, Range2), VarMap) ->
{Domain, VarMap1} = t_unify(Domain1, Domain2, VarMap),
@@ -3104,6 +3421,23 @@ t_unify(?tuple_set(List1) = T1, ?tuple_set(List2) = T2, VarMap) ->
{Tuples, NewVarMap} -> {t_sup(Tuples), NewVarMap}
catch _:_ -> throw({mismatch, T1, T2})
end;
+t_unify(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, VarMap0) ->
+ {DefK, VarMap1} = t_unify(ADefK, BDefK, VarMap0),
+ {DefV, VarMap2} = t_unify(ADefV, BDefV, VarMap1),
+ {Pairs, VarMap} =
+ map_pairwise_merge_foldr(
+ fun(K, MNess, V1, MNess, V2, {Pairs0, VarMap3}) ->
+ %% We know that the keys unify and do not contain variables, or they
+ %% would not be singletons
+ %% TODO: Should V=?none (known missing keys) be handled special?
+ {V, VarMap4} = t_unify(V1, V2, VarMap3),
+ {[{K,MNess,V}|Pairs0], VarMap4};
+ (K, _, V1, _, V2, {Pairs0, VarMap3}) ->
+ %% One mandatory and one optional; what should be done in this case?
+ {V, VarMap4} = t_unify(V1, V2, VarMap3),
+ {[{K,?mand,V}|Pairs0], VarMap4}
+ end, {[], VarMap2}, A, B),
+ {t_map(Pairs, DefK, DefV), VarMap};
t_unify(?opaque(_) = T1, ?opaque(_) = T2, VarMap) ->
t_unify(t_opaque_structure(T1), t_opaque_structure(T2), VarMap);
t_unify(T1, ?opaque(_) = T2, VarMap) ->
@@ -3307,7 +3641,7 @@ t_subtract_list(T, []) ->
-spec t_subtract(erl_type(), erl_type()) -> erl_type().
t_subtract(_, ?any) -> ?none;
-t_subtract(_, ?var(_)) -> ?none;
+t_subtract(T, ?var(_)) -> T;
t_subtract(?any, _) -> ?any;
t_subtract(?var(_) = T, _) -> T;
t_subtract(T, ?unit) -> T;
@@ -3460,8 +3794,50 @@ t_subtract(?product(Elements1) = T1, ?product(Elements2)) ->
_ -> T1
end
end;
-t_subtract(?map(_) = T, _) -> % XXX: very crude; will probably need refinement
- T;
+t_subtract(?map(APairs, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B) ->
+ case t_is_subtype(ADefK, BDefK) andalso t_is_subtype(ADefV, BDefV) of
+ false -> A;
+ true ->
+ %% We fold over the maps to produce a list of constraints, where
+ %% constraints are additional key-value pairs to put in Pairs. Only one
+ %% constraint need to be applied to produce a type that excludes the
+ %% right-hand-side type, so if more than one constraint is produced, we
+ %% just return the left-hand-side argument.
+ %%
+ %% Each case of the fold may either conclude that
+ %% * The arguments constrain A at least as much as B, i.e. that A so far
+ %% is a subtype of B. In that case they return false
+ %% * That for the particular arguments, A being a subtype of B does not
+ %% hold, but the infinimum of A and B is nonempty, and by narrowing a
+ %% pair in A, we can create a type that excludes some elements in the
+ %% infinumum. In that case, they will return that pair.
+ %% * That for the particular arguments, A being a subtype of B does not
+ %% hold, and either the infinumum of A and B is empty, or it is not
+ %% possible with the current representation to create a type that
+ %% excludes elements from B without also excluding elements that are
+ %% only in A. In that case, it will return the pair from A unchanged.
+ case
+ map_pairwise_merge(
+ %% If V1 is a subtype of V2, the case that K does not exist in A
+ %% remain.
+ fun(K, ?opt, V1, ?mand, V2) -> {K, ?opt, t_subtract(V1, V2)};
+ (K, _, V1, _, V2) ->
+ %% If we subtract an optional key, that leaves a mandatory key
+ case t_subtract(V1, V2) of
+ ?none -> false;
+ Partial -> {K, ?mand, Partial}
+ end
+ end, A, B)
+ of
+ %% We produce a list of keys that are constrained. As only one of
+ %% these should apply at a time, we can't represent the difference if
+ %% more than one constraint is produced. If we applied all of them,
+ %% that would make an underapproximation, which we must not do.
+ [] -> ?none; %% A is a subtype of B
+ [E] -> t_map(mapdict_store(E, APairs), ADefK, ADefV);
+ _ -> A
+ end
+ end;
t_subtract(?product(P1), _) ->
?product(P1);
t_subtract(T, ?product(_)) ->
@@ -3592,6 +3968,11 @@ subtype_is_equal(T1, T2) ->
t_is_instance(ConcreteType, Type) ->
t_is_subtype(ConcreteType, t_unopaque(Type)).
+-spec t_do_overlap(erl_type(), erl_type()) -> boolean().
+
+t_do_overlap(TypeA, TypeB) ->
+ not (t_is_none_or_unit(t_inf(TypeA, TypeB))).
+
-spec t_unopaque(erl_type()) -> erl_type().
t_unopaque(T) ->
@@ -3622,12 +4003,17 @@ t_unopaque(?union([A,B,F,I,L,N,T,M,O,Map]), Opaques) ->
UL = t_unopaque(L, Opaques),
UT = t_unopaque(T, Opaques),
UF = t_unopaque(F, Opaques),
+ UM = t_unopaque(M, 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,UMap])|UO]);
+ t_sup([?union([A,B,UF,I,UL,N,UT,UM,OF,UMap])|UO]);
+t_unopaque(?map(Pairs,DefK,DefV), Opaques) ->
+ t_map([{K, MNess, t_unopaque(V, Opaques)} || {K, MNess, V} <- Pairs],
+ t_unopaque(DefK, Opaques),
+ t_unopaque(DefV, Opaques));
t_unopaque(T, _) ->
T.
@@ -3679,6 +4065,16 @@ t_limit_k(?opaque(Es), K) ->
Opaque#opaque{struct = NewS}
end || #opaque{struct = S} = Opaque <- set_to_list(Es)],
?opaque(ordsets:from_list(List));
+t_limit_k(?map(Pairs0, DefK0, DefV0), K) ->
+ Fun = fun({EK, MNess, EV}, {Exact, DefK1, DefV1}) ->
+ LV = t_limit_k(EV, K - 1),
+ case t_limit_k(EK, K - 1) of
+ EK -> {[{EK,MNess,LV}|Exact], DefK1, DefV1};
+ LK -> {Exact, t_sup(LK, DefK1), t_sup(LV, DefV1)}
+ end
+ end,
+ {Pairs, DefK2, DefV2} = lists:foldr(Fun, {[], DefK0, DefV0}, Pairs0),
+ t_map(Pairs, t_limit_k(DefK2, K - 1), t_limit_k(DefV2, K - 1));
t_limit_k(T, _K) -> T.
%%============================================================================
@@ -3753,6 +4149,9 @@ t_map(Fun, ?opaque(Set)) ->
[] -> ?none;
_ -> ?opaque(ordsets:from_list(L))
end);
+t_map(Fun, ?map(Pairs,DefK,DefV)) ->
+ %% TODO:
+ Fun(t_map(Pairs, Fun(DefK), Fun(DefV)));
t_map(Fun, T) ->
Fun(T).
@@ -3894,8 +4293,23 @@ t_to_string(?float, _RecDict) -> "float()";
t_to_string(?number(?any, ?unknown_qual), _RecDict) -> "number()";
t_to_string(?product(List), RecDict) ->
"<" ++ comma_sequence(List, RecDict) ++ ">";
-t_to_string(?map(Pairs), RecDict) ->
- "#{" ++ map_pairs_to_string(Pairs,RecDict) ++ "}";
+t_to_string(?map([],?any,?any), _RecDict) -> "map()";
+t_to_string(?map(Pairs0,DefK,DefV), RecDict) ->
+ {Pairs, ExtraEl} =
+ case {DefK, DefV} of
+ {?none, ?none} -> {Pairs0, []};
+ {?any, ?any} -> {Pairs0, ["..."]};
+ _ -> {Pairs0 ++ [{DefK,?opt,DefV}], []}
+ end,
+ Tos = fun(T) -> case T of
+ ?any -> "_";
+ _ -> t_to_string(T, RecDict)
+ end end,
+ StrMand = [{Tos(K),Tos(V)}||{K,?mand,V}<-Pairs],
+ StrOpt = [{Tos(K),Tos(V)}||{K,?opt,V}<-Pairs],
+ "#{" ++ string:join([K ++ ":=" ++ V||{K,V}<-StrMand]
+ ++ [K ++ "=>" ++ V||{K,V}<-StrOpt]
+ ++ ExtraEl, ", ") ++ "}";
t_to_string(?tuple(?any, ?any, ?any), _RecDict) -> "tuple()";
t_to_string(?tuple(Elements, _Arity, ?any), RecDict) ->
"{" ++ comma_sequence(Elements, RecDict) ++ "}";
@@ -3916,12 +4330,6 @@ t_to_string(?var(Id), _RecDict) when is_integer(Id) ->
flat_format("var(~w)", [Id]).
-map_pairs_to_string([],_) -> [];
-map_pairs_to_string(Pairs,RecDict) ->
- StrPairs = [{t_to_string(K,RecDict),t_to_string(V,RecDict)}||{K,V}<-Pairs],
- string:join([K ++ "=>" ++ V||{K,V}<-StrPairs], ", ").
-
-
record_to_string(Tag, [_|Fields], FieldNames, RecDict) ->
FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []),
"#" ++ atom_to_string(Tag) ++ "{" ++ string:join(FieldStrings, ",") ++ "}".
@@ -4010,7 +4418,7 @@ mod_name(Mod, Name) ->
site(), mod_records()) -> erl_type().
t_from_form(Form, ExpTypes, Site, RecDict) ->
- t_from_form(Form, ExpTypes, Site, RecDict, dict:new()).
+ t_from_form(Form, ExpTypes, Site, RecDict, maps:new()).
-spec t_from_form(parse_form(), sets:set(mfa()),
site(), mod_records(), var_table()) -> erl_type().
@@ -4027,7 +4435,7 @@ t_from_form_without_remote(Form, Site, TypeTable) ->
Module = site_module(Site),
RecDict = dict:from_list([{Module, TypeTable}]),
ExpTypes = replace_by_none,
- {T, _} = t_from_form1(Form, ExpTypes, Site, RecDict, dict:new()),
+ {T, _} = t_from_form1(Form, ExpTypes, Site, RecDict, maps:new()),
T.
%% REC_TYPE_LIMIT is used for limiting the depth of recursive types.
@@ -4083,7 +4491,7 @@ t_from_form(_, _TypeNames, _ET, _S, _MR, _V, D, L) when D =< 0 ; L =< 0 ->
t_from_form({var, _L, '_'}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_any(), L};
t_from_form({var, _L, Name}, _TypeNames, _ET, _S, _MR, V, _D, L) ->
- case dict:find(Name, V) of
+ case maps:find(Name, V) of
error -> {t_var(Name), L};
{ok, Val} -> {Val, L}
end;
@@ -4149,7 +4557,7 @@ t_from_form({type, _L, 'fun', [{type, _, any}, Range]}, TypeNames,
t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},
TypeNames, ET, S, MR, V, D, L) ->
{Dom1, L1} = list_from_form(Domain, TypeNames, ET, S, MR, V, D, L),
- {Ran1, L2} = t_from_form(Range, TypeNames, ET, S, MR, V, D - 1, L1),
+ {Ran1, L2} = t_from_form(Range, TypeNames, ET, S, MR, V, D, L1),
{t_fun(Dom1, Ran1), L2};
t_from_form({type, _L, identifier, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_identifier(), L};
@@ -4164,8 +4572,26 @@ t_from_form({type, _L, list, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
t_from_form({type, _L, list, [Type]}, TypeNames, ET, S, MR, V, D, L) ->
{T, L1} = t_from_form(Type, TypeNames, ET, S, MR, V, D - 1, L - 1),
{t_list(T), L1};
-t_from_form({type, _L, map, _}, TypeNames, ET, S, MR, V, D, L) ->
- builtin_type(map, t_map([]), TypeNames, ET, S, MR, V, D, L);
+t_from_form({type, _L, map, any}, TypeNames, ET, S, MR, V, D, L) ->
+ builtin_type(map, t_map(), TypeNames, ET, S, MR, V, D, L);
+t_from_form({type, _L, map, List}, TypeNames, ET, S, MR, V, D, L) ->
+ {Pairs1, L5} =
+ fun PairsFromForm(_, L1) when L1 =< 0 -> {[{?any,?opt,?any}], L1};
+ PairsFromForm([], L1) -> {[], L1};
+ PairsFromForm([{type, _, Oper, [KF, VF]}|T], L1) ->
+ {Key, L2} = t_from_form(KF, TypeNames, ET, S, MR, V, D - 1, L1),
+ {Val, L3} = t_from_form(VF, TypeNames, ET, S, MR, V, D - 1, L2),
+ {Pairs0, L4} = PairsFromForm(T, L3 - 1),
+ case Oper of
+ map_field_assoc -> {[{Key,?opt, Val}|Pairs0], L4};
+ map_field_exact -> {[{Key,?mand,Val}|Pairs0], L4}
+ end
+ end(List, L),
+ try
+ {Pairs, DefK, DefV} = map_from_form(Pairs1, [], [], [], ?none, ?none),
+ {t_map(Pairs, DefK, DefV), L5}
+ catch none -> {t_none(), L5}
+ end;
t_from_form({type, _L, mfa, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_mfa(), L};
t_from_form({type, _L, module, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
@@ -4284,7 +4710,7 @@ type_from_form(Name, Args, TypeNames, ET, Site0, MR, V, D, L) ->
{ArgTypes, L1} =
list_from_form(Args, TypeNames, ET, Site0, MR, V, D, L),
List = lists:zip(ArgNames, ArgTypes),
- TmpV = dict:from_list(List),
+ TmpV = maps:from_list(List),
Site = TypeName,
t_from_form(Form, NewTypeNames, ET, Site, MR, TmpV, D, L1);
false ->
@@ -4297,7 +4723,7 @@ type_from_form(Name, Args, TypeNames, ET, Site0, MR, V, D, L) ->
{ArgTypes, L1} =
list_from_form(Args, NewTypeNames, ET, Site0, MR, V, D, L),
List = lists:zip(ArgNames, ArgTypes),
- TmpV = dict:from_list(List),
+ TmpV = maps:from_list(List),
Site = TypeName,
{Rep, L2} =
t_from_form(Form, NewTypeNames, ET, Site, MR, TmpV, D, L1),
@@ -4339,7 +4765,7 @@ remote_from_form(RemMod, Name, Args, TypeNames, ET, S, MR, V, D, L) ->
{ArgTypes, L1} = list_from_form(Args, TypeNames,
ET, S, MR, V, D, L),
List = lists:zip(ArgNames, ArgTypes),
- TmpVarDict = dict:from_list(List),
+ TmpVarDict = maps:from_list(List),
Site = RemType,
t_from_form(Form, NewTypeNames, ET,
Site, MR, TmpVarDict, D, L1);
@@ -4353,7 +4779,7 @@ remote_from_form(RemMod, Name, Args, TypeNames, ET, S, MR, V, D, L) ->
{ArgTypes, L1} = list_from_form(Args, NewTypeNames,
ET, S, MR, V, D, L),
List = lists:zip(ArgNames, ArgTypes),
- TmpVarDict = dict:from_list(List),
+ TmpVarDict = maps:from_list(List),
Site = RemType,
{NewRep, L2} =
t_from_form(Form, NewTypeNames, ET, Site, MR,
@@ -4424,7 +4850,7 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, ET, S, MR, V, D, L) ->
{ok, NewFields} ->
{NewFields1, L2} =
fields_from_form(NewFields, NewTypeNames, ET, S1, MR,
- dict:new(), D, L1),
+ maps:new(), D, L1),
Rec = t_tuple(
[t_atom(Name)|[Type
|| {_FieldName, Type} <- NewFields1]]),
@@ -4495,11 +4921,55 @@ list_from_form([H|Tail], TypeNames, ET, S, MR, V, D, L) ->
{T1, L2} = list_from_form(Tail, TypeNames, ET, S, MR, V, D, L1),
{[H1|T1], L2}.
+%% Sorts, combines non-singleton pairs, and applies precendence and
+%% mandatoriness rules.
+map_from_form([], ShdwPs, MKs, Pairs, DefK, DefV) ->
+ verify_possible(MKs, ShdwPs),
+ {promote_to_mand(MKs, Pairs), DefK, DefV};
+map_from_form([{SKey,MNess,Val}|SPairs], ShdwPs0, MKs0, Pairs0, DefK0, DefV0) ->
+ Key = lists:foldl(fun({K,_},S)->t_subtract(S,K)end, SKey, ShdwPs0),
+ ShdwPs = case Key of ?none -> ShdwPs0; _ -> [{Key,Val}|ShdwPs0] end,
+ MKs = case MNess of ?mand -> [SKey|MKs0]; ?opt -> MKs0 end,
+ if MNess =:= ?mand, SKey =:= ?none -> throw(none);
+ true -> ok
+ end,
+ {Pairs, DefK, DefV} =
+ case is_singleton_type(Key) of
+ true ->
+ MNess1 = case Val =:= ?none of true -> ?opt; false -> MNess end,
+ {mapdict_insert({Key,MNess1,Val}, Pairs0), DefK0, DefV0};
+ false ->
+ case Key =:= ?none orelse Val =:= ?none of
+ true -> {Pairs0, DefK0, DefV0};
+ false -> {Pairs0, t_sup(DefK0, Key), t_sup(DefV0, Val)}
+ end
+ end,
+ map_from_form(SPairs, ShdwPs, MKs, Pairs, DefK, DefV).
+
+%% Verifies that all mandatory keys are possible, throws 'none' otherwise
+verify_possible(MKs, ShdwPs) ->
+ lists:foreach(fun(M) -> verify_possible_1(M, ShdwPs) end, MKs).
+
+verify_possible_1(M, ShdwPs) ->
+ case lists:any(fun({K,_}) -> t_inf(M, K) =/= ?none end, ShdwPs) of
+ true -> ok;
+ false -> throw(none)
+ end.
+
+-spec promote_to_mand([erl_type()], t_map_dict()) -> t_map_dict().
+
+promote_to_mand(_, []) -> [];
+promote_to_mand(MKs, [E={K,_,V}|T]) ->
+ [case lists:any(fun(M) -> t_is_equal(K,M) end, MKs) of
+ true -> {K, ?mand, V};
+ false -> E
+ end|promote_to_mand(MKs, T)].
+
-spec t_check_record_fields(parse_form(), sets:set(mfa()), site(),
mod_records()) -> ok.
t_check_record_fields(Form, ExpTypes, Site, RecDict) ->
- t_check_record_fields(Form, ExpTypes, Site, RecDict, dict:new()).
+ t_check_record_fields(Form, ExpTypes, Site, RecDict, maps:new()).
-spec t_check_record_fields(parse_form(), sets:set(mfa()), site(),
mod_records(), var_table()) -> ok.
@@ -4627,8 +5097,13 @@ t_form_to_string({type, _L, iodata, []}) -> "iodata()";
t_form_to_string({type, _L, iolist, []}) -> "iolist()";
t_form_to_string({type, _L, list, [Type]}) ->
"[" ++ t_form_to_string(Type) ++ "]";
-t_form_to_string({type, _L, map, _}) ->
- "#{}";
+t_form_to_string({type, _L, map, any}) -> "map()";
+t_form_to_string({type, _L, map, Args}) ->
+ "#{" ++ string:join(t_form_to_string_list(Args), ",") ++ "}";
+t_form_to_string({type, _L, map_field_assoc, [Key, Val]}) ->
+ t_form_to_string(Key) ++ "=>" ++ t_form_to_string(Val);
+t_form_to_string({type, _L, map_field_exact, [Key, Val]}) ->
+ t_form_to_string(Key) ++ ":=" ++ t_form_to_string(Val);
t_form_to_string({type, _L, mfa, []}) -> "mfa()";
t_form_to_string({type, _L, module, []}) -> "module()";
t_form_to_string({type, _L, node, []}) -> "node()";
@@ -4663,8 +5138,9 @@ t_form_to_string({type, _L, Name, []} = T) ->
D0 = dict:new(),
MR = dict:from_list([{M, D0}]),
S = {type, {M,Name,0}},
+ V = #{},
{T1, _} =
- t_from_form(T, [], sets:new(), S, MR, D0, _Deep=1000, _ALot=100000),
+ t_from_form(T, [], sets:new(), S, MR, V, _Deep=1000, _ALot=100000),
t_to_string(T1)
catch throw:{error, _} -> atom_to_string(Name) ++ "()"
end;
@@ -4789,11 +5265,70 @@ do_opaque(?union(List) = Type, Opaques, Pred) ->
do_opaque(Type, _Opaques, Pred) ->
Pred(Type).
-map_keys(?map(Pairs)) ->
- [K || {K, _} <- Pairs].
+map_all_values(?map(Pairs,_,DefV)) ->
+ [DefV|[V || {V, _, _} <- Pairs]].
+
+map_all_keys(?map(Pairs,DefK,_)) ->
+ [DefK|[K || {_, _, K} <- Pairs]].
+
+map_all_types(M) ->
+ map_all_keys(M) ++ map_all_values(M).
+
+%% Tests if a type has exactly one possible value.
+-spec t_is_singleton(erl_type()) -> boolean().
+
+t_is_singleton(Type) ->
+ t_is_singleton(Type, 'universe').
+
+-spec t_is_singleton(erl_type(), opaques()) -> boolean().
+
+t_is_singleton(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_singleton_type/1).
+
+%% Incomplete; not all representable singleton types are included.
+is_singleton_type(?nil) -> true;
+is_singleton_type(?atom(?any)) -> false;
+is_singleton_type(?atom(Set)) ->
+ ordsets:size(Set) =:= 1;
+is_singleton_type(?int_range(V, V)) -> true;
+is_singleton_type(?int_set(Set)) ->
+ ordsets:size(Set) =:= 1;
+is_singleton_type(?tuple(Types, Arity, _)) when is_integer(Arity) ->
+ lists:all(fun is_singleton_type/1, Types);
+is_singleton_type(?tuple_set([{Arity, [OnlyTuple]}])) when is_integer(Arity) ->
+ is_singleton_type(OnlyTuple);
+is_singleton_type(?map(Pairs, ?none, ?none)) ->
+ lists:all(fun({_,MNess,V}) -> MNess =:= ?mand andalso is_singleton_type(V)
+ end, Pairs);
+is_singleton_type(_) ->
+ false.
+
+%% Returns the only possible value of a singleton type.
+-spec t_singleton_to_term(erl_type(), opaques()) -> term().
+
+t_singleton_to_term(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun singleton_type_to_term/1).
-map_values(?map(Pairs)) ->
- [V || {_, V} <- Pairs].
+singleton_type_to_term(?nil) -> [];
+singleton_type_to_term(?atom(Set)) when Set =/= ?any ->
+ case ordsets:size(Set) of
+ 1 -> hd(ordsets:to_list(Set));
+ _ -> error(badarg)
+ end;
+singleton_type_to_term(?int_range(V, V)) -> V;
+singleton_type_to_term(?int_set(Set)) ->
+ case ordsets:size(Set) of
+ 1 -> hd(ordsets:to_list(Set));
+ _ -> error(badarg)
+ end;
+singleton_type_to_term(?tuple(Types, Arity, _)) when is_integer(Arity) ->
+ lists:map(fun singleton_type_to_term/1, Types);
+singleton_type_to_term(?tuple_set([{Arity, [OnlyTuple]}]))
+ when is_integer(Arity) ->
+ singleton_type_to_term(OnlyTuple);
+singleton_type_to_term(?map(Pairs, ?none, ?none)) ->
+ maps:from_list([{singleton_type_to_term(K), singleton_type_to_term(V)}
+ || {K,?mand,V} <- Pairs]).
%% -----------------------------------
%% Set
diff --git a/lib/hipe/test/bs_SUITE_data/bs_match_compiler.erl b/lib/hipe/test/bs_SUITE_data/bs_match_compiler.erl
new file mode 100644
index 0000000000..4cb48ff57e
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_match_compiler.erl
@@ -0,0 +1,1235 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_match_compiler.erl
+%%%
+%%%-------------------------------------------------------------------
+-module(bs_match_compiler).
+-compile(nowarn_shadow_vars).
+
+-export([test/0]).
+-export([exported_id/1, exported_id/2]). %% needed by a test
+
+test() ->
+ Funs = [fun fun_shadow/0, fun int_float/0, fun otp_5269/0, fun null_fields/0,
+ fun wiger/0, fun bin_tail/0, fun save_restore/0,
+ fun partitioned_bs_match/0, fun function_clause/0, fun unit/0,
+ fun shared_sub_bins/0, fun bin_and_float/0, fun dec_subidentifiers/0,
+ fun skip_optional_tag/0, fun wfbm/0, fun degenerated_match/0,
+ fun bs_sum/0, fun coverage/0, fun multiple_uses/0, fun zero_label/0,
+ fun followed_by_catch/0, fun matching_meets_construction/0,
+ fun simon/0, fun matching_and_andalso/0,
+ fun otp_7188/0, fun otp_7233/0, fun otp_7240/0, fun otp_7498/0,
+ fun match_string/0, fun zero_width/0, fun bad_size/0, fun haystack/0,
+ fun cover_beam_bool/0, fun matched_out_size/0, fun follow_fail_br/0,
+ fun no_partition/0, fun calling_a_binary/0, fun binary_in_map/0,
+ fun match_string_opt/0, fun map_and_binary/0,
+ fun unsafe_branch_caching/0],
+ lists:foreach(fun (F) -> ok = F() end, Funs).
+
+
+%%--------------------------------------------------------------------
+%% OTP-5270
+
+fun_shadow() ->
+ 7 = fun_shadow_1(),
+ 7 = fun_shadow_2(8),
+ 7 = fun_shadow_3(),
+ no = fun_shadow_4(8),
+ ok.
+
+fun_shadow_1() ->
+ L = 8,
+ F = fun(<<L:L,B:L>>) -> B end,
+ F(<<16:8, 7:16>>).
+
+fun_shadow_2(L) ->
+ F = fun(<<L:L,B:L>>) -> B end,
+ F(<<16:8, 7:16>>).
+
+fun_shadow_3() ->
+ L = 8,
+ F = fun(<<L:L,B:L,L:L>>) -> B end,
+ F(<<16:8, 7:16,16:16>>).
+
+fun_shadow_4(L) ->
+ F = fun(<<L:L,B:L,L:L>>) -> B;
+ (_) -> no end,
+ F(<<16:8, 7:16,15:16>>).
+
+%%--------------------------------------------------------------------
+%% OTP-5323
+
+int_float() ->
+ <<103133.0:64/float>> = <<103133:64/float>>,
+ <<103133:64/float>> = <<103133:64/float>>,
+ ok.
+
+%%--------------------------------------------------------------------
+%% Stolen from erl_eval_SUITE and modified.
+%% OTP-5269. Bugs in the bit syntax.
+
+otp_5269() ->
+ check(fun() -> L = 8, F = fun(<<A:L,B:A>>) -> B end, F(<<16:8, 7:16>>) end, 7),
+ check(fun() -> L = 8, <<A:L,B:A>> = <<16:8, 7:16>>, B end, 7),
+ check(fun() -> U = 8, (fun(<<U:U>>) -> U end)(<<32:8>>) end, 32),
+ check(fun() -> U = 8, [U || <<U:U>> <- [<<32:8>>]] end, [32]),
+ check(fun() -> [X || <<A:8, B:A>> <- [<<16:8,19:16>>],
+ <<X:8>> <- [<<B:8>>]] end,
+ [19]),
+ check(fun() -> A = 4, B = 28, _ = bit_size(<<13:(A+(X=B))>>), X end, 28),
+ check(fun() ->
+ <<Size,B:Size/binary,Rest/binary>> = <<2,"AB","CD">>,
+ {Size,B,Rest}
+ end, {2,<<"AB">>,<<"CD">>}),
+ check(fun() -> X = 32, [X || <<X:X>> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end,
+ %% "binsize variable" ^
+ [1,2]),
+ check(fun() ->
+ (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) ->
+ case A of
+ B -> wrong;
+ _ -> ok
+ end
+ end)(<<1,2,3,4>>) end,
+ ok),
+ ok.
+
+%%--------------------------------------------------------------------
+
+null_fields() ->
+ check(fun() ->
+ W = id(0),
+ F = fun(<<_:W>>) -> tail;
+ (<<>>) -> empty
+ end,
+ F(<<>>)
+ end, tail),
+ check(fun() ->
+ F = fun(<<_/binary>>) -> tail;
+ (<<>>) -> empty
+ end,
+ F(<<>>)
+ end, tail),
+ ok.
+
+%%--------------------------------------------------------------------
+
+wiger() ->
+ ok1 = wcheck(<<3>>),
+ ok2 = wcheck(<<1,2,3>>),
+ ok3 = wcheck(<<4>>),
+ {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>),
+ {error,<<>>} = wcheck(<<>>),
+ ok.
+
+wcheck(<<A>>) when A==3->
+ ok1;
+wcheck(<<_,_:2/binary>>) ->
+ ok2;
+wcheck(<<_>>) ->
+ ok3;
+wcheck(Other) ->
+ {error,Other}.
+
+%%--------------------------------------------------------------------
+
+bin_tail() ->
+ S = <<"abcde">>,
+ $a = bin_tail_c(S, 0),
+ $c = bin_tail_c(S, 2),
+ $e = bin_tail_c(S, 4),
+ {'EXIT',_} = (catch bin_tail_c(S, 5)),
+ {'EXIT',_} = (catch bin_tail_c_var(S, 5)),
+
+ $a = bin_tail_d(S, 0),
+ $b = bin_tail_d(S, 8),
+ $d = bin_tail_d(S, 3*8),
+ {'EXIT',_} = (catch bin_tail_d_dead(S, 1)),
+ {'EXIT',_} = (catch bin_tail_d_dead(S, 9)),
+ {'EXIT',_} = (catch bin_tail_d_dead(S, 5*8)),
+ {'EXIT',_} = (catch bin_tail_d_var(S, 1)),
+
+ ok = bin_tail_e(<<2:2,0:1,1:5>>),
+ ok = bin_tail_e(<<2:2,1:1,1:5,42:64>>),
+ error = bin_tail_e(<<3:2,1:1,1:5,42:64>>),
+ error = bin_tail_e(<<>>),
+ ok.
+
+bin_tail_c(Bin, Offset) ->
+ Res = bin_tail_c_dead(Bin, Offset),
+ <<_:Offset/binary,_,Tail/binary>> = Bin,
+ {Res,Tail} = bin_tail_c_var(Bin, Offset),
+ Res.
+
+bin_tail_c_dead(Bin, Offset) ->
+ <<_:Offset/binary,C,_/binary>> = Bin,
+ C.
+
+bin_tail_c_var(Bin, Offset) ->
+ <<_:Offset/binary,C,Tail/binary>> = Bin,
+ {C,Tail}.
+
+bin_tail_d(Bin, BitOffset) ->
+ Res = bin_tail_d_dead(Bin, BitOffset),
+ <<_:BitOffset,_:8,Tail/binary>> = Bin,
+ {Res,Tail} = bin_tail_d_var(Bin, BitOffset),
+ Res.
+
+bin_tail_d_dead(Bin, BitOffset) ->
+ <<_:BitOffset,C,_/binary>> = Bin,
+ C.
+
+bin_tail_d_var(Bin, BitOffset) ->
+ <<_:BitOffset,C,Tail/binary>> = Bin,
+ {C,Tail}.
+
+bin_tail_e(Bin) ->
+ case bin_tail_e_dead(Bin) of
+ ok ->
+ <<_,Tail/binary>> = Bin,
+ Tail = bin_tail_e_var(Bin),
+ ok;
+ error ->
+ bin_tail_e_var(Bin)
+ end.
+
+bin_tail_e_dead(Bin) ->
+ case Bin of
+ %% The binary is aligned at the end; neither the bs_skip_bits2 nor
+ %% bs_test_tail2 instructions are needed.
+ <<2:2,_:1,1:5,_/binary>> -> ok;
+ _ -> error
+ end.
+
+bin_tail_e_var(Bin) ->
+ case Bin of
+ %% The binary is aligned at the end; neither the bs_skip_bits2 nor
+ %% bs_test_tail2 instructions are needed.
+ <<2:2,_:1,1:5,Tail/binary>> -> Tail;
+ _ -> error
+ end.
+
+%%--------------------------------------------------------------------
+
+save_restore() ->
+ 0 = save_restore_1(<<0:2,42:6>>),
+ {1,3456} = save_restore_1(<<1:2,3456:14>>),
+ {2,7981234} = save_restore_1(<<2:2,7981234:30>>),
+ {3,763967493838} = save_restore_1(<<0:2,763967493838:62>>),
+
+ A = <<" x">>,
+ B = <<".x">>,
+ C = <<"-x">>,
+
+ {" ",<<"x">>} = lll(A),
+ {" ",<<"x">>} = mmm(A),
+ {" ",<<"x">>} = nnn(A),
+ {" ",<<"x">>} = ooo(A),
+
+ {".",<<"x">>} = lll(B),
+ {".",<<"x">>} = mmm(B),
+ {".",<<"x">>} = nnn(B),
+ {".",<<"x">>} = ooo(B),
+
+ {"-",<<"x">>} = lll(C),
+ {"-",<<"x">>} = mmm(C),
+ {"-",<<"x">>} = nnn(C),
+ {"-",<<"x">>} = ooo(C),
+
+ Bin = <<-1:64>>,
+ case bad_float_unpack_match(Bin) of
+ -1 -> ok;
+ _Other -> bad_return_value_probably_NaN
+ end.
+
+save_restore_1(Bin) ->
+ case Bin of
+ <<0:2,_:6>> -> 0;
+ <<1:2,A:14>> -> {1,A};
+ <<2:2,A:30>> -> {2,A};
+ <<A:64>> -> {3,A}
+ end.
+
+lll(<<Char, Tail/binary>>) -> {[Char],Tail}.
+
+mmm(<<$.,$.,$., Tail/binary>>) -> Tail;
+mmm(<<$\s,$-,$\s, Tail/binary>>) -> Tail;
+mmm(<<Char, Tail/binary>>) -> {[Char],Tail}. %% Buggy Tail!
+
+nnn(<<"...", Tail/binary>>) -> Tail;
+nnn(<<" - ", Tail/binary>>) -> Tail;
+nnn(<<Char, Tail/binary>>) -> {[Char],Tail}. %% Buggy Tail!
+
+ooo(<<" - ", Tail/binary>>) -> Tail;
+ooo(<<Char, Tail/binary>>) -> {[Char],Tail}.
+
+bad_float_unpack_match(<<F:64/float>>) -> F;
+bad_float_unpack_match(<<I:64/integer-signed>>) -> I.
+
+%%--------------------------------------------------------------------
+
+partitioned_bs_match() ->
+ <<1,2,3>> = partitioned_bs_match(blurf, <<42,1,2,3>>),
+ error = partitioned_bs_match(10, <<7,8,15,13>>),
+ error = partitioned_bs_match(100, {a,tuple,is,'not',a,binary}),
+ ok = partitioned_bs_match(0, <<>>),
+ fc(partitioned_bs_match, [-1,blurf],
+ catch partitioned_bs_match(-1, blurf)),
+ fc(partitioned_bs_match, [-1,<<1,2,3>>],
+ catch partitioned_bs_match(-1, <<1,2,3>>)),
+ {17,<<1,2,3>>} = partitioned_bs_match_2(1, <<17,1,2,3>>),
+ {7,<<1,2,3>>} = partitioned_bs_match_2(7, <<17,1,2,3>>),
+
+ fc(partitioned_bs_match_2, [4,<<0:17>>],
+ catch partitioned_bs_match_2(4, <<0:17>>)),
+
+ anything = partitioned_bs_match_3(anything, <<42>>),
+ ok = partitioned_bs_match_3(1, 2),
+ ok.
+
+partitioned_bs_match(_, <<42:8,T/binary>>) -> T;
+partitioned_bs_match(N, _) when N > 0 -> error;
+partitioned_bs_match(_, <<>>) -> ok.
+
+partitioned_bs_match_2(1, <<B:8,T/binary>>) -> {B,T};
+partitioned_bs_match_2(Len, <<_:8,T/binary>>) -> {Len,T}.
+
+partitioned_bs_match_3(Var, <<_>>) -> Var;
+partitioned_bs_match_3(1, 2) -> ok.
+
+%%--------------------------------------------------------------------
+
+function_clause() ->
+ ok = function_clause_1(<<0,7,0,7,42>>),
+ fc(function_clause_1, [<<0,1,2,3>>],
+ catch function_clause_1(<<0,1,2,3>>)),
+ fc(function_clause_1, [<<0,1,2,3>>],
+ catch function_clause_1(<<0,7,0,1,2,3>>)),
+
+ ok = function_clause_2(<<0,7,0,7,42>>),
+ ok = function_clause_2(<<255>>),
+ ok = function_clause_2(<<13:4>>),
+ fc(function_clause_2, [<<0,1,2,3>>],
+ catch function_clause_2(<<0,1,2,3>>)),
+ fc(function_clause_2, [<<0,1,2,3>>],
+ catch function_clause_2(<<0,7,0,1,2,3>>)),
+ ok.
+
+function_clause_1(<<0:8,7:8,T/binary>>) ->
+ function_clause_1(T);
+function_clause_1(<<_:8>>) ->
+ ok.
+
+function_clause_2(<<0:8,7:8,T/binary>>) ->
+ function_clause_2(T);
+function_clause_2(<<_:8>>) ->
+ ok;
+function_clause_2(<<_:4>>) ->
+ ok.
+
+%%--------------------------------------------------------------------
+
+unit() ->
+ 42 = peek1(<<42>>),
+ 43 = peek1(<<43,1,2>>),
+ 43 = peek1(<<43,1,2,(-1):1>>),
+ 43 = peek1(<<43,1,2,(-1):2>>),
+ 43 = peek1(<<43,1,2,(-1):7>>),
+
+ 99 = peek8(<<99>>),
+ 100 = peek8(<<100,101>>),
+ fc(peek8, [<<100,101,0:1>>], catch peek8(<<100,101,0:1>>)),
+
+ 37484 = peek16(<<37484:16>>),
+ 37489 = peek16(<<37489:16,5566:16>>),
+ fc(peek16, [<<8>>], catch peek16(<<8>>)),
+ fc(peek16, [<<42:15>>], catch peek16(<<42:15>>)),
+ fc(peek16, [<<1,2,3,4,5>>], catch peek16(<<1,2,3,4,5>>)),
+
+ 127 = peek7(<<127:7>>),
+ 100 = peek7(<<100:7,19:7>>),
+ fc(peek7, [<<1,2>>], catch peek7(<<1,2>>)),
+ ok.
+
+peek1(<<B:8,_/bitstring>>) -> B.
+
+peek7(<<B:7,_/binary-unit:7>>) -> B.
+
+peek8(<<B:8,_/binary>>) -> B.
+
+peek16(<<B:16,_/binary-unit:16>>) -> B.
+
+%%--------------------------------------------------------------------
+
+shared_sub_bins() ->
+ {15,[<<>>,<<5>>,<<4,5>>,<<3,4,5>>,<<2,3,4,5>>]} = sum(<<1,2,3,4,5>>, [], 0),
+ ok.
+
+sum(<<B,T/binary>>, Acc, Sum) ->
+ sum(T, [T|Acc], Sum+B);
+sum(<<>>, Last, Sum) -> {Sum,Last}.
+
+%%--------------------------------------------------------------------
+
+bin_and_float() ->
+ 14.0 = bin_and_float(<<1.0/float,2.0/float,3.0/float>>, 0.0),
+ ok.
+
+bin_and_float(<<X/float,Y/float,Z/float,T/binary>>, Sum) when is_float(X),
+ is_float(Y),
+ is_float(Z) ->
+ bin_and_float(T, Sum+X*X+Y*Y+Z*Z);
+bin_and_float(<<>>, Sum) -> Sum.
+
+%%--------------------------------------------------------------------
+
+dec_subidentifiers() ->
+ {[],<<1,2,3>>} =
+ do_dec_subidentifiers(<<1:1,42:7,1:1,99:7,1,2,3>>, 0, [], 2),
+ {[5389],<<1,2,3>>} =
+ do_dec_subidentifiers(<<1:1,42:7,0:1,13:7,1,2,3>>, 0, [], 2),
+ {[3,2,1],not_a_binary} = dec_subidentifiers(not_a_binary, any, [1,2,3], 0),
+ ok.
+
+do_dec_subidentifiers(Buffer, Av, Al, Len) ->
+ Res = dec_subidentifiers(Buffer, Av, Al, Len),
+ Res = dec_subidentifiers2(Buffer, Av, Al, Len),
+ Res = dec_subidentifiers4(Buffer, Av, Al, Len),
+ Res = dec_subidentifiers3(Buffer, Av, Al, Len).
+
+dec_subidentifiers(Buffer, _Av, Al, 0) ->
+ {lists:reverse(Al),Buffer};
+dec_subidentifiers(<<1:1,H:7,T/binary>>, Av, Al, Len) ->
+ dec_subidentifiers(T, (Av bsl 7) bor H, Al, Len-1);
+dec_subidentifiers(<<H,T/binary>>, Av, Al, Len) ->
+ dec_subidentifiers(T, 0, [((Av bsl 7) bor H)|Al], Len-1).
+
+dec_subidentifiers2(<<Buffer/binary>>, _Av, Al, 0) ->
+ {lists:reverse(Al),Buffer};
+dec_subidentifiers2(<<1:1,H:7,T/binary>>, Av, Al, Len) ->
+ dec_subidentifiers2(T, (Av bsl 7) bor H, Al, Len-1);
+dec_subidentifiers2(<<H,T/binary>>, Av, Al, Len) ->
+ dec_subidentifiers2(T, 0, [((Av bsl 7) bor H)|Al], Len-1).
+
+dec_subidentifiers3(Buffer, _Av, Al, 0) when is_binary(Buffer) ->
+ {lists:reverse(Al),Buffer};
+dec_subidentifiers3(<<1:1,H:7,T/binary>>, Av, Al, Len) ->
+ dec_subidentifiers3(T, (Av bsl 7) bor H, Al, Len-1);
+dec_subidentifiers3(<<H,T/binary>>, Av, Al, Len) ->
+ dec_subidentifiers3(T, 0, [((Av bsl 7) bor H)|Al], Len-1).
+
+dec_subidentifiers4(<<1:1,H:7,T/binary>>, Av, Al, Len) when Len =/= 0 ->
+ dec_subidentifiers4(T, (Av bsl 7) bor H, Al, Len-1);
+dec_subidentifiers4(<<H,T/binary>>, Av, Al, Len) when Len =/= 0 ->
+ dec_subidentifiers4(T, 0, [((Av bsl 7) bor H)|Al], Len-1);
+dec_subidentifiers4(Buffer, _Av, Al, 0) ->
+ {lists:reverse(Al),Buffer}.
+
+%%--------------------------------------------------------------------
+
+skip_optional_tag() ->
+ {ok,<<>>} = skip_optional_tag(<<42>>, <<42>>),
+ {ok,<<>>} = skip_optional_tag(<<42,1>>, <<42,1>>),
+ {ok,<<1,2,3>>} = skip_optional_tag(<<42>>, <<42,1,2,3>>),
+ missing = skip_optional_tag(<<2:3>>, blurf),
+ ok.
+
+skip_optional_tag(<<>>, Binary) ->
+ {ok,Binary};
+skip_optional_tag(<<Tag,RestTag/binary>>, <<Tag,Rest/binary>>) ->
+ skip_optional_tag(RestTag, Rest);
+skip_optional_tag(_, _) -> missing.
+
+%%--------------------------------------------------------------------
+
+-define(DATELEN, 16).
+
+wfbm() ->
+ %% check_for_dot_or_space and get_tail is from wfbm4 by Steve Vinoski,
+ %% with modifications.
+ {nomatch,0} = check_for_dot_or_space(<<" ">>),
+ {nomatch,0} = check_for_dot_or_space(<<" abc">>),
+ {ok,<<"abcde">>} = check_for_dot_or_space(<<"abcde 34555">>),
+ {nomatch,0} = check_for_dot_or_space(<<".gurka">>),
+ {nomatch,1} = check_for_dot_or_space(<<"g.urka">>),
+ nomatch = get_tail(<<>>),
+ {ok,<<"2007/10/23/blurf">>} = get_tail(<<"200x/2007/10/23/blurf ">>),
+ {skip,?DATELEN+5} = get_tail(<<"200x/2007/10/23/blurf.">>),
+ nomatch = get_tail(<<"200y.2007.10.23.blurf ">>),
+ {'EXIT',_} = (catch get_tail({no,binary,at,all})),
+ {'EXIT',_} = (catch get_tail(no_binary)),
+ ok.
+
+check_for_dot_or_space(Bin) ->
+ check_for_dot_or_space(Bin, 0).
+
+check_for_dot_or_space(<<$\s, _/binary>>, 0) ->
+ {nomatch,0};
+check_for_dot_or_space(Bin, Len) ->
+ case Bin of
+ <<Front:Len/binary, $\s, _/binary>> ->
+ {ok,Front};
+ <<_:Len/binary, $., _/binary>> ->
+ {nomatch,Len};
+ _ ->
+ check_for_dot_or_space(Bin, Len+1)
+ end.
+
+get_tail(<<>>) ->
+ nomatch;
+get_tail(Bin) ->
+ <<Front:?DATELEN/binary, Tail/binary>> = Bin,
+ case Front of
+ <<_:3/binary,"x/",Y:4/binary,$/,M:2/binary,$/,D:2/binary,$/>> ->
+ case check_for_dot_or_space(Tail) of
+ {ok,Match} ->
+ {ok,<<Y/binary,$/,M/binary,$/,D/binary,$/, Match/binary>>};
+ {nomatch,Skip} -> {skip,?DATELEN + Skip}
+ end;
+ _ -> nomatch
+ end.
+
+%%--------------------------------------------------------------------
+
+degenerated_match() ->
+ error = degenerated_match_1(<<>>),
+ 1 = degenerated_match_1(<<1:1>>),
+ 2 = degenerated_match_1(<<42,43>>),
+
+ error = degenerated_match_2(<<>>),
+ no_split = degenerated_match_2(<<1,2>>),
+ {<<1,2,3,4>>,<<5>>} = degenerated_match_2(<<1,2,3,4,5>>),
+ ok.
+
+degenerated_match_1(<<>>) -> error;
+degenerated_match_1(Bin) -> byte_size(Bin).
+
+degenerated_match_2(<<>>) -> error;
+degenerated_match_2(Bin) ->
+ case byte_size(Bin) > 4 of
+ true -> split_binary(Bin, 4);
+ false -> no_split
+ end.
+
+%%--------------------------------------------------------------------
+
+bs_sum() ->
+ 0 = bs_sum_1([]),
+ 0 = bs_sum_1(<<>>),
+ 42 = bs_sum_1([42]),
+ 1 = bs_sum_1(<<1>>),
+ 10 = bs_sum_1([1,2,3,4]),
+ 15 = bs_sum_1(<<1,2,3,4,5>>),
+ 21 = bs_sum_1([1,2,3|<<4,5,6>>]),
+ 15 = bs_sum_1([1,2,3|{4,5}]),
+ 6 = bs_sum_1([1,2,3|zero]),
+ 6 = bs_sum_1([1,2,3|0]),
+ 7 = bs_sum_1([1,2,3|one]),
+
+ fc(catch bs_sum_1({too,big,tuple})),
+ fc(catch bs_sum_1([1,2,3|{too,big,tuple}])),
+
+ [] = sneaky_alias(<<>>),
+ [559,387655] = sneaky_alias(id(<<559:32,387655:32>>)),
+ fc(sneaky_alias, [<<1>>], catch sneaky_alias(id(<<1>>))),
+ fc(sneaky_alias, [[1,2,3,4]], catch sneaky_alias(lists:seq(1, 4))),
+ ok.
+
+bs_sum_1(<<H,T/binary>>) -> H+bs_sum_1(T);
+bs_sum_1([H|T]) -> H+bs_sum_1(T);
+bs_sum_1({A,B}=_Tuple=_AliasForNoGoodReason) -> A+B;
+bs_sum_1(0) -> 0;
+bs_sum_1(zero=_Zero) -> 0;
+bs_sum_1(one) -> 1;
+bs_sum_1([]) -> 0;
+bs_sum_1(<<>>) -> 0.
+
+sneaky_alias(<<>>=L) -> binary_to_list(L);
+sneaky_alias(<<From:32,L/binary>>) -> [From|sneaky_alias(L)].
+
+%%--------------------------------------------------------------------
+
+coverage() ->
+ 0 = coverage_fold(fun(B, A) -> A+B end, 0, <<>>),
+ 6 = coverage_fold(fun(B, A) -> A+B end, 0, <<1,2,3>>),
+ fc(catch coverage_fold(fun(B, A) -> A+B end, 0, [a,b,c])),
+
+ {<<42.0:64/float>>,float} = coverage_build(<<>>, <<42>>, float),
+ {<<>>,not_a_tuple} = coverage_build(<<>>, <<>>, not_a_tuple),
+ {<<16#76,"abc",16#A9,"abc">>,{x,42,43}} =
+ coverage_build(<<>>, <<16#7,16#A>>, {x,y,z}),
+
+ [<<2>>,<<1>>] = coverage_bc(<<1,2>>, []),
+
+ {x,<<"abc">>,z} = coverage_setelement(<<2,"abc">>, {x,y,z}),
+
+ [42] = coverage_apply(<<42>>, [exported_id]),
+ 42 = coverage_external(<<42>>),
+
+ do_coverage_bin_to_term_list([]),
+ do_coverage_bin_to_term_list([lists:seq(0, 10),{a,b,c},<<23:42>>]),
+ fc(coverage_bin_to_term_list, [<<0,0,0,7>>],
+ catch do_coverage_bin_to_term_list_1(<<7:32>>)),
+
+ <<>> = coverage_per_key(<<4:32>>),
+ <<$a,$b,$c>> = coverage_per_key(<<7:32,"abc">>),
+
+ ok.
+
+coverage_fold(Fun, Acc, <<H,T/binary>>) ->
+ IdFun = fun id/1,
+ coverage_fold(Fun, Fun(IdFun(H), IdFun(Acc)), T);
+coverage_fold(Fun, Acc, <<>>) when is_function(Fun, 2) -> Acc.
+
+coverage_build(Acc0, <<H,T/binary>>, float) ->
+ Float = id(<<H:64/float>>),
+ Acc = <<Acc0/binary,Float/binary>>,
+ coverage_build(Acc, T, float);
+coverage_build(Acc0, <<H,T/binary>>, Tuple0) ->
+ Str = id(<<H:(id(4)),(H-1):4,"abc">>),
+ Acc = id(<<Acc0/bitstring,Str/bitstring>>),
+ Tuple = setelement(2, setelement(3, Tuple0, 43), 42),
+ if
+ byte_size(Acc) > 0 ->
+ coverage_build(Acc, T, Tuple)
+ end;
+coverage_build(Acc, <<>>, Tuple) -> {Acc,Tuple}.
+
+coverage_bc(<<H,T/binary>>, Acc) ->
+ B = << <<C:8>> || C <- [H] >>,
+ coverage_bc(T, [B|Acc]);
+coverage_bc(<<>>, Acc) -> Acc.
+
+coverage_setelement(<<H,T1/binary>>, Tuple) when element(1, Tuple) =:= x ->
+ setelement(H, Tuple, T1).
+
+coverage_apply(<<H,T/binary>>, [F|Fs]) ->
+ [?MODULE:F(H)|coverage_apply(T, Fs)];
+coverage_apply(<<>>, []) -> [].
+
+coverage_external(<<H,T/binary>>) ->
+ ?MODULE:exported_id(T, T),
+ H.
+
+exported_id(I) -> id(I).
+
+exported_id(_, _) -> ok.
+
+do_coverage_bin_to_term_list(L) ->
+ Bin = << <<(begin BinTerm = term_to_binary(Term),
+ <<(byte_size(BinTerm)):32,BinTerm/binary>> end)/binary>> ||
+ Term <- L >>,
+ L = do_coverage_bin_to_term_list_1(Bin),
+ L = do_coverage_bin_to_term_list_1(<<Bin/binary,7:32,"garbage">>),
+ L = do_coverage_bin_to_term_list_1(<<7:32,"garbage",Bin/binary>>).
+
+do_coverage_bin_to_term_list_1(Bin) ->
+ Res = coverage_bin_to_term_list(Bin),
+ Res = coverage_bin_to_term_list(Bin, []),
+ Res = coverage_bin_to_term_list_catch(Bin),
+ Res = coverage_bin_to_term_list_catch(Bin, []).
+
+coverage_bin_to_term_list(<<Sz:32,BinTerm:Sz/binary,T/binary>>) ->
+ try binary_to_term(BinTerm) of
+ Term -> [Term|coverage_bin_to_term_list(T)]
+ catch
+ error:badarg -> coverage_bin_to_term_list(T)
+ end;
+coverage_bin_to_term_list(<<>>) -> [].
+
+coverage_bin_to_term_list(<<Sz:32,BinTerm:Sz/binary,T/binary>>, Acc) ->
+ try binary_to_term(BinTerm) of
+ Term -> coverage_bin_to_term_list(T, [Term|Acc])
+ catch
+ error:badarg -> coverage_bin_to_term_list(T, Acc)
+ end;
+coverage_bin_to_term_list(<<>>, Acc) -> lists:reverse(Acc).
+
+coverage_bin_to_term_list_catch(<<Sz:32,BinTerm:Sz/binary,T/binary>>) ->
+ case catch binary_to_term(BinTerm) of
+ {'EXIT',_} -> coverage_bin_to_term_list_catch(T);
+ Term -> [Term|coverage_bin_to_term_list_catch(T)]
+ end;
+coverage_bin_to_term_list_catch(<<>>) -> [].
+
+coverage_bin_to_term_list_catch(<<Sz:32,BinTerm:Sz/binary,T/binary>>, Acc) ->
+ case catch binary_to_term(BinTerm) of
+ {'EXIT',_} -> coverage_bin_to_term_list_catch(T, Acc);
+ Term -> coverage_bin_to_term_list_catch(T, [Term|Acc])
+ end;
+coverage_bin_to_term_list_catch(<<>>, Acc) -> lists:reverse(Acc).
+
+coverage_per_key(<<BinSize:32,Bin/binary>> = B) ->
+ true = (byte_size(B) =:= BinSize),
+ Bin.
+
+%%--------------------------------------------------------------------
+
+multiple_uses() ->
+ {344,62879,345,<<245,159,1,89>>} = multiple_uses_1(<<1,88,245,159,1,89>>),
+ true = multiple_uses_2(<<0,0,197,18>>),
+ <<42,43>> = multiple_uses_3(<<0,0,42,43>>, fun id/1),
+ ok.
+
+multiple_uses_1(<<X:16,Tail/binary>>) ->
+ %% NOT OPTIMIZED: sub binary is matched or used in more than one place
+ {Y,Z} = multiple_uses_match(Tail),
+ {X,Y,Z,Tail}.
+
+multiple_uses_2(<<_:16,Tail/binary>>) ->
+ %% NOT OPTIMIZED: sub binary is matched or used in more than one place
+ multiple_uses_cmp(Tail, Tail).
+
+multiple_uses_3(<<_:16,Tail/binary>>, Fun) ->
+ %% NOT OPTIMIZED: sub binary is used or returned
+ Fun(Tail).
+
+multiple_uses_match(<<Y:16,Z:16>>) -> {Y,Z}.
+
+multiple_uses_cmp(<<Y:16>>, <<Y:16>>) -> true;
+multiple_uses_cmp(<<_:16>>, <<_:16>>) -> false.
+
+%%--------------------------------------------------------------------
+
+zero_label() ->
+ <<"nosemouth">> = read_pols(<<"FACE","nose","mouth">>),
+ <<"CE">> = read_pols(<<"noFACE">>),
+ ok.
+
+read_pols(Data) ->
+ <<PolygonType:4/binary,Rest/binary>> = Data,
+ %% Intentional warning.
+ _ = (PolygonType == <<"FACE">>) or (PolygonType == <<"PTCH">>),
+ Rest.
+
+%%--------------------------------------------------------------------
+
+followed_by_catch() ->
+ ok = handle(<<0,1,2,3,4,5>>).
+
+-record(rec,{field}).
+handle(<<>>) -> ok;
+handle(Msg) ->
+ <<_DataLen:16, Rest/binary>> = Msg,
+ case catch fooX:func() of
+ [X] ->
+ X#rec.field;
+ _ ->
+ ok
+ end,
+ handle(Rest).
+
+%%--------------------------------------------------------------------
+
+matching_meets_construction() ->
+ Bin = id(<<"abc">>),
+ Len = id(2),
+ Tail0 = id(<<1,2,3,4,5>>),
+ <<_:Len/binary,Tail/binary>> = Tail0,
+ Res = <<Tail/binary,Bin/binary>>,
+ <<3,4,5,"abc">> = Res,
+ {'EXIT',{badarg,_}} = (catch matching_meets_construction_1(<<"Abc">>)),
+ {'EXIT',{badarg,_}} = (catch matching_meets_construction_2(<<"Abc">>)),
+ <<"Bbc">> = matching_meets_construction_3(<<"Abc">>),
+ <<1,2>> = encode_octet_string(<<1,2,3>>, 2),
+ ok.
+
+matching_meets_construction_1(<<"A",H/binary>>) -> <<"B",H>>.
+
+matching_meets_construction_2(<<"A",H/binary>>) -> <<"B",H/float>>.
+
+matching_meets_construction_3(<<"A",H/binary>>) -> <<"B",H/binary>>.
+
+encode_octet_string(<<OctetString/binary>>, Len) ->
+ <<OctetString:Len/binary-unit:8>>.
+
+%%--------------------------------------------------------------------
+
+simon() ->
+ one = simon(blurf, <<>>),
+ two = simon(0, <<42>>),
+ fc(simon, [17,<<1>>], catch simon(17, <<1>>)),
+ fc(simon, [0,<<1,2,3>>], catch simon(0, <<1,2,3>>)),
+
+ one = simon2(blurf, <<9>>),
+ two = simon2(0, <<9,1>>),
+ fc(simon2, [0,<<9,10,11>>], catch simon2(0, <<9,10,11>>)),
+ ok.
+
+simon(_, <<>>) -> one;
+simon(0, <<_>>) -> two.
+
+simon2(_, <<9>>) -> one;
+simon2(0, <<_:16>>) -> two.
+
+%%--------------------------------------------------------------------
+%% OTP-7113: Crash in v3_codegen.
+
+matching_and_andalso() ->
+ ok = matching_and_andalso_1(<<1,2,3>>, 3),
+ {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, -8)),
+ {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, blurf)),
+ {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, 19)),
+
+ {"abc",<<"xyz">>} = matching_and_andalso_2("abc", <<"-xyz">>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($a-1)>>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($z+1)>>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($A-1)>>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($Z+1)>>),
+ error = matching_and_andalso_2([], <<>>),
+ error = matching_and_andalso_2([], <<$A>>),
+ error = matching_and_andalso_2([], <<$Z>>),
+ error = matching_and_andalso_2([], <<$a>>),
+ error = matching_and_andalso_2([], <<$z>>),
+ ok.
+
+matching_and_andalso_1(<<Bitmap/binary>>, K)
+ when is_integer(K) andalso size(Bitmap) >= K andalso 0 < K -> ok.
+
+matching_and_andalso_2(Datetime, <<H,T/binary>>)
+ when not ((H >= $a) andalso (H =< $z)) andalso
+ not ((H >= $A) andalso (H =< $Z)) ->
+ {Datetime,T};
+matching_and_andalso_2(_, _) -> error.
+
+%%--------------------------------------------------------------------
+%% Thanks to Tomas Stejskal.
+
+otp_7188() ->
+ MP3 = <<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>>,
+ {ok,{"ID3v1",
+ [{title,<<68,117,154,105,232,107,121>>},
+ {artist,<<"Daniel Landa">>},
+ {album,<<"Best Of">>}]}} = parse_v1_or_v11_tag(MP3),
+ ok.
+
+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.
+
+%%--------------------------------------------------------------------
+%% OTP-7233. Record and binary matching optimizations clashed.
+%% Thanks to Vladimir Klebansky.
+
+-record(rec_otp_7233, {key, val}).
+
+otp_7233() ->
+ otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[{"xxxxxxxx",42}]}),
+ [<<"XXabcde">>,{"xxxxxxxx",42}] = get(io_format),
+ erase(io_format),
+ otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[]}),
+ undefined = get(io_format),
+ ok.
+
+otp_7233_1(Rec) ->
+ <<K:2/binary,_Rest:5/binary>> = Rec#rec_otp_7233.key,
+ case K of
+ <<"XX">> ->
+ Value = Rec#rec_otp_7233.val,
+ case lists:keysearch("xxxxxxxx", 1, Value) of
+ {value,T} -> put(io_format, [Rec#rec_otp_7233.key,T]);
+ false -> ok
+ end;
+ _ -> ok
+ end.
+
+%%--------------------------------------------------------------------
+
+otp_7240() ->
+ a = otp_7240_a(0, <<>>),
+ b = otp_7240_a(1, 2),
+
+ a = otp_7240_b(anything, <<>>),
+ b = otp_7240_b(1, {x,y}),
+
+ a = otp_7240_c(anything, <<>>),
+ b = otp_7240_c(1, <<2>>),
+
+ a = otp_7240_d(anything, <<>>),
+ b = otp_7240_d(again, <<2>>),
+
+ a = otp_7240_e(anything, <<>>),
+ b = otp_7240_e(1, 41),
+
+ a = otp_7240_f(anything, <<>>),
+ b = otp_7240_f(1, {}),
+
+ ok.
+
+otp_7240_a(_, <<>>) -> a;
+otp_7240_a(1, 2) -> b.
+
+otp_7240_b(_, <<>>) -> a;
+otp_7240_b(1, {_,_}) -> b.
+
+otp_7240_c(_, <<>>) -> a;
+otp_7240_c(1, <<2>>) -> b.
+
+otp_7240_d(_, <<>>) -> a;
+otp_7240_d(_, <<2>>) -> b.
+
+otp_7240_e(_, <<>>) -> a;
+otp_7240_e(1, B) when B < 42 -> b.
+
+otp_7240_f(_, <<>>) -> a;
+otp_7240_f(1, B) when is_tuple(B) -> b.
+
+%%--------------------------------------------------------------------
+
+otp_7498() ->
+ <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 0),
+ <<2,3>> = otp_7498_foo(<<1,2,3>>, 1),
+ <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 2),
+
+ <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 0),
+ <<2,3>> = otp_7498_bar(<<1,2,3>>, 1),
+ <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 2),
+ <<>> = otp_7498_bar(<<>>, 2),
+ <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 3),
+ ok.
+
+otp_7498_foo(Bin, 0) ->
+ otp_7498_foo(Bin, 42);
+otp_7498_foo(<<_A, Rest/bitstring>>, 1) ->
+ otp_7498_foo(Rest, 43);
+otp_7498_foo(Bin, _I) ->
+ Bin.
+
+otp_7498_bar(Bin, 0) ->
+ otp_7498_bar(Bin, 42);
+otp_7498_bar(<<_A, Rest/bitstring>>, 1) ->
+ otp_7498_bar(Rest, 43);
+otp_7498_bar(<<>>, 2) ->
+ otp_7498_bar(<<>>, 44);
+otp_7498_bar(Bin, _I) ->
+ Bin.
+
+%%--------------------------------------------------------------------
+
+match_string() ->
+ %% To make sure that native endian really is handled correctly
+ %% (i.e. that the compiler does not attempt to use bs_match_string/4
+ %% instructions for native segments), running this test is not enough.
+ %% Either examine the generated for do_match_string_native/1 or
+ %% check the coverage for the v3_kernel module.
+ case erlang:system_info(endian) of
+ little ->
+ do_match_string_native(<<$a,0,$b,0>>);
+ big ->
+ do_match_string_native(<<0,$a,0,$b>>)
+ end,
+ do_match_string_big(<<0,$a,0,$b>>),
+ do_match_string_little(<<$a,0,$b,0>>),
+
+ do_match_string_big_signed(<<255,255>>),
+ do_match_string_little_signed(<<255,255>>),
+
+ plain = no_match_string_opt(<<"abc">>),
+ strange = no_match_string_opt(<<$a:9,$b:9,$c:9>>),
+ ok.
+
+do_match_string_native(<<$a:16/native,$b:16/native>>) -> ok.
+
+do_match_string_big(<<$a:16/big,$b:16/big>>) -> ok.
+
+do_match_string_little(<<$a:16/little,$b:16/little>>) -> ok.
+
+do_match_string_big_signed(<<(-1):16/signed>>) -> ok.
+
+do_match_string_little_signed(<<(-1):16/little-signed>>) -> ok.
+
+no_match_string_opt(<<"abc">>) -> plain;
+no_match_string_opt(<<$a:9,$b:9,$c:9>>) -> strange.
+
+%%--------------------------------------------------------------------
+%% OTP-7591: A zero-width segment in matching would crash the compiler.
+
+zero_width() ->
+ <<Len:16/little, Str:Len/binary, 0:0>> = <<2, 0, $h, $i, 0:0>>,
+ 2 = Len,
+ Str = <<"hi">>,
+ %% Match sure that values that cannot fit in a segment will not match.
+ case id(<<0:8>>) of
+ <<256:8>> -> error;
+ _ -> ok
+ end.
+
+%%--------------------------------------------------------------------
+%% OTP_7650: A invalid size for binary segments could crash the compiler.
+
+bad_size() ->
+ Tuple = {a,b,c},
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Tuple>> = id(<<>>)),
+ Binary = <<1,2,3>>,
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Binary>> = id(<<>>)),
+ ok.
+
+%%--------------------------------------------------------------------
+
+haystack() ->
+ <<0:10/unit:8>> = haystack_1(<<0:10/unit:8>>),
+ [<<0:10/unit:8>>,
+ <<0:20/unit:8>>] = haystack_2(<<1:8192>>),
+ ok.
+
+%% Used to crash the compiler.
+haystack_1(Haystack) ->
+ Subs = [10],
+ [begin
+ <<B:Y/binary>> = Haystack,
+ B
+ end || Y <- Subs],
+ Haystack.
+
+%% There would be an incorrect badmatch exception.
+haystack_2(Haystack) ->
+ Subs = [{687,10},{369,20}],
+ [begin
+ <<_:X/binary,B:Y/binary,_/binary>> = Haystack,
+ B
+ end || {X,Y} <- Subs].
+
+fc({'EXIT',{function_clause,_}}) -> ok.
+
+fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Args,_}|_]}}) -> ok;
+fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Arity,_}|_]}})
+ when length(Args) =:= Arity ->
+ true = test_server:is_native(?MODULE).
+
+%%--------------------------------------------------------------------
+%% Cover the clause handling bs_context to binary in
+%% beam_block:initialized_regs/2.
+cover_beam_bool() ->
+ ok = do_cover_beam_bool(<<>>, 3),
+ <<19>> = do_cover_beam_bool(<<19>>, 2),
+ <<42>> = do_cover_beam_bool(<<42>>, 1),
+ <<17>> = do_cover_beam_bool(<<13,17>>, 0),
+ ok.
+
+do_cover_beam_bool(Bin, X) when X > 0 ->
+ if
+ X =:= 1; X =:= 2 ->
+ Bin;
+ true ->
+ ok
+ end;
+do_cover_beam_bool(<<_,Bin/binary>>, X) ->
+ do_cover_beam_bool(Bin, X+1).
+
+%%--------------------------------------------------------------------
+
+matched_out_size() ->
+ {253,16#DEADBEEF} = mos_int(<<8,253,16#DEADBEEF:32>>),
+ {6,16#BEEFDEAD} = mos_int(<<3,6:3,16#BEEFDEAD:32>>),
+ {53,16#CAFEDEADBEEFCAFE} = mos_int(<<16,53:16,16#CAFEDEADBEEFCAFE:64>>),
+ {23,16#CAFEDEADBEEFCAFE} = mos_int(<<5,23:5,16#CAFEDEADBEEFCAFE:64>>),
+
+ {<<1,2,3>>,4} = mos_bin(<<3,1,2,3,4,3>>),
+ {<<1,2,3,7>>,19,42} = mos_bin(<<4,1,2,3,7,19,4,42>>),
+ <<1,2,3,7>> = mos_bin(<<4,1,2,3,7,"abcdefghij">>),
+ ok.
+
+mos_int(<<L,I:L,X:32>>) ->
+ {I,X};
+mos_int(<<L,I:L,X:64>>) ->
+ {I,X}.
+
+mos_bin(<<L,Bin:L/binary,X:8,L>>) ->
+ L = byte_size(Bin),
+ {Bin,X};
+mos_bin(<<L,Bin:L/binary,X:8,L,Y:8>>) ->
+ L = byte_size(Bin),
+ {Bin,X,Y};
+mos_bin(<<L,Bin:L/binary,"abcdefghij">>) ->
+ L = byte_size(Bin),
+ Bin.
+
+%%--------------------------------------------------------------------
+
+follow_fail_br() ->
+ 42 = ffb_1(<<0,1>>, <<0>>),
+ 8 = ffb_1(<<0,1>>, [a]),
+ 42 = ffb_2(<<0,1>>, <<0>>, 17),
+ 8 = ffb_2(<<0,1>>, [a], 0),
+ ok.
+
+ffb_1(<<_,T/bitstring>>, List) ->
+ case List of
+ <<_>> ->
+ 42;
+ [_|_] ->
+ %% The fail branch of the bs_start_match2 instruction pointing
+ %% to here would be ignored, making the compiler incorrectly
+ %% assume that the delayed sub-binary optimization was safe.
+ bit_size(T)
+ end.
+
+ffb_2(<<_,T/bitstring>>, List, A) ->
+ case List of
+ <<_>> when A =:= 17 -> 42;
+ [_|_] -> bit_size(T)
+ end.
+
+%%--------------------------------------------------------------------
+
+no_partition() ->
+ one = no_partition_1(<<"string">>, a1),
+ {two,<<"string">>} = no_partition_1(<<"string">>, a2),
+ {two,<<>>} = no_partition_1(<<>>, a2),
+ {two,a} = no_partition_1(a, a2),
+ three = no_partition_1(undefined, a3),
+ {four,a,[]} = no_partition_1([a], a4),
+ {five,a,b} = no_partition_1({a,b}, a5),
+
+ one = no_partition_2(<<"string">>, a1),
+ two = no_partition_2(<<"string">>, a2),
+ two = no_partition_2(<<>>, a2),
+ two = no_partition_2(a, a2),
+ three = no_partition_2(undefined, a3),
+ four = no_partition_2(42, a4),
+ five = no_partition_2([], a5),
+ six = no_partition_2(42.0, a6),
+ ok.
+
+no_partition_1(<<"string">>, a1) -> one;
+no_partition_1(V, a2) -> {two,V};
+no_partition_1(undefined, a3) -> three;
+no_partition_1([H|T], a4) -> {four,H,T};
+no_partition_1({A,B}, a5) -> {five,A,B}.
+
+no_partition_2(<<"string">>, a1) -> one;
+no_partition_2(_, a2) -> two;
+no_partition_2(undefined, a3) -> three;
+no_partition_2(42, a4) -> four;
+no_partition_2([], a5) -> five;
+no_partition_2(42.0, a6) -> six.
+
+%%--------------------------------------------------------------------
+
+calling_a_binary() ->
+ [] = 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>>).
+
+%%--------------------------------------------------------------------
+
+binary_in_map() ->
+ ok = match_binary_in_map(#{key => <<42:8>>}),
+ {'EXIT',{{badmatch,#{key := 1}},_}} =
+ (catch match_binary_in_map(#{key => 1})),
+ {'EXIT',{{badmatch,#{key := <<1023:16>>}},_}} =
+ (catch match_binary_in_map(#{key => <<1023:16>>})),
+ {'EXIT',{{badmatch,#{key := <<1:8>>}},_}} =
+ (catch match_binary_in_map(#{key => <<1:8>>})),
+ {'EXIT',{{badmatch,not_a_map},_}} =
+ (catch match_binary_in_map(not_a_map)),
+ ok.
+
+match_binary_in_map(Map) ->
+ case 8 of
+ N ->
+ #{key := <<42:N>>} = Map,
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+
+match_string_opt() ->
+ {x,<<1,2,3>>,{<<1>>,{v,<<1,2,3>>}}} = match_string_opt({<<1>>,{v,<<1,2,3>>}}),
+ ok.
+
+match_string_opt({<<1>>,{v,V}}=T) ->
+ {x,V,T}.
+
+%%--------------------------------------------------------------------
+%% If 'bin_opt_info' was given the warning would lack filename and
+%% line number.
+
+map_and_binary() ->
+ {<<"10">>,<<"37">>,<<"am">>} = do_map_and_binary(<<"10:37am">>),
+ Map1 = #{time => "noon"},
+ {ok,Map1} = do_map_and_binary(Map1),
+ Map2 = #{hour => 8, min => 42},
+ {8,42,Map2} = do_map_and_binary(Map2),
+ ok.
+
+do_map_and_binary(<<Hour:2/bytes, $:, Min:2/bytes, Rest/binary>>) ->
+ {Hour, Min, Rest};
+do_map_and_binary(#{time := _} = T) ->
+ {ok, T};
+do_map_and_binary(#{hour := Hour, min := Min} = T) ->
+ {Hour, Min, T}.
+
+%%--------------------------------------------------------------------
+%% Unsafe caching of branch outcomes in beam_bsm would cause the
+%% delayed creation of sub-binaries optimization to be applied even
+%% when it was unsafe.
+
+unsafe_branch_caching() ->
+ <<>> = do_unsafe_branch_caching(<<42,1>>),
+ <<>> = do_unsafe_branch_caching(<<42,2>>),
+ <<>> = do_unsafe_branch_caching(<<42,3>>),
+ <<17,18>> = do_unsafe_branch_caching(<<42,3,17,18>>),
+ <<>> = do_unsafe_branch_caching(<<1,3,42,2>>),
+ ok.
+
+do_unsafe_branch_caching(<<Code/integer, Bin/binary>>) ->
+ <<C1/integer, B1/binary>> = Bin,
+ case C1 of
+ X when X =:= 1 orelse X =:= 2 ->
+ Bin2 = <<>>;
+ _ ->
+ Bin2 = B1
+ end,
+ case Code of
+ 1 -> do_unsafe_branch_caching(Bin2);
+ _ -> Bin2
+ end.
+
+%%--------------------------------------------------------------------
+
+check(F, R) ->
+ R = F().
+
+id(I) -> I.
diff --git a/lib/hipe/test/hipe_SUITE.erl b/lib/hipe/test/hipe_SUITE.erl
index 9319b710d9..a5b3924aa8 100644
--- a/lib/hipe/test/hipe_SUITE.erl
+++ b/lib/hipe/test/hipe_SUITE.erl
@@ -19,9 +19,6 @@
-compile([export_all]).
-include_lib("common_test/include/ct.hrl").
-suite() ->
- [{ct_hooks, [ts_install_cth]}].
-
all() ->
[app, appup].
diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl
index 9f5d7421b4..64c5c0a7c9 100644
--- a/lib/hipe/test/hipe_testsuite_driver.erl
+++ b/lib/hipe/test/hipe_testsuite_driver.erl
@@ -107,7 +107,7 @@ write_suite(Suite) ->
write_header(#suite{suitename = SuiteName, outputfile = OutputFile,
testcases = TestCases}) ->
Exports = format_export(TestCases),
- TimeLimit = 2, %% with 1 it fails on some slow machines...
+ TimeLimit = 3, %% with 1 or 2 it fails on some slow machines...
io:format(OutputFile,
"%% ATTENTION!\n"
"%% This is an automatically generated file. Do not edit.\n\n"
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index ae567ea185..deef010e54 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -23,10 +23,6 @@
</legalnotice>
<title>mod_esi</title>
- <prepared>Joakim Greben&ouml;</prepared>
- <docno></docno>
- <date>1997-10-14</date>
- <rev>2.2</rev>
<file>mod_esi.sgml</file>
</header>
<module>mod_esi</module>
@@ -39,6 +35,56 @@
<marker id="deliver"></marker>
</description>
+ <section>
+ <title>DATA TYPES</title>
+ <p>The following data types are used in the functions for mod_esi:</p>
+
+ <taglist>
+ <tag><c>env() = </c></tag>
+ <item> <p><c>{EnvKey()::atom(), Value::term()}</c></p>
+ </item>
+
+ <p>Currently supported key value pairs</p>
+ <taglist>
+
+ <tag><c>{server_software, string()}</c></tag>
+ <item><p>Indicates the inets version.</p></item>
+
+ <tag><c>{server_name, string()}</c></tag>
+ <item><p>The local hostname. </p></item>
+
+ <tag><c>{gateway_interface, string()}</c></tag>
+ <item><p>Legacy string used in CGI, just ignore.</p> </item>
+
+ <tag><c>{server_protocol, string()}</c></tag>
+ <item><p> HTTP version, currently "HTTP/1.1"</p></item>
+
+ <tag>{server_port, integer()}</tag>
+ <item><p>Servers port number.</p></item>
+
+ <tag><c>{request_method, "GET | "PUT" | "DELETE | "POST" | "PATCH"}</c></tag>
+
+ <tag><c>{remote_adress, inet:ip_address()} </c></tag>
+ <item><p>The clients ip address.</p></item>
+
+ <tag><c>{peer_cert, undefined | no_peercert | DER:binary()</c></tag>
+ <item>
+ <p>For TLS connections where client certificates are used this will
+ be an ASN.1 DER-encoded X509-certificate as an Erlang binary.
+ If client certificates are not used the value will be <c>no_peercert</c>,
+ and if TLS is not used (HTTP or connection is lost due to network failure)
+ the value will be <c>undefined</c>.
+ </p></item>
+
+ <tag><c>{script_name, string()}</c></tag>
+ <item><p>Request URI</p></item>
+
+ <tag><c>{http_LowerCaseHTTPHeaderName, string()}</c></tag>
+ <item><p>example: {http_content_type, "text/html"}</p></item>
+ </taglist>
+
+ </taglist>
+
<funcs>
<func>
<name>deliver(SessionID, Data) -> ok | {error, Reason}</name>
@@ -63,11 +109,11 @@
overhead. Do not assume anything about the data type of
<c>SessionID</c>. <c>SessionID</c> must be the value given
as input to the ESI callback function that you implemented.</p>
- </note>
+ </note>
</desc>
</func>
</funcs>
-
+ </section>
<section>
<title>ESI Callback Functions</title>
</section>
@@ -78,9 +124,7 @@
to the server process by calling <c>mod_esi:deliver/2</c>.</fsummary>
<type>
<v>SessionID = term()</v>
- <v>Env = [EnvironmentDirectives] ++ ParsedHeader</v>
- <v>EnvironmentDirectives = {Key,Value}</v>
- <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name</v>
+ <v>Env = env()</v>
<v>Input = string()</v>
</type>
<desc>
@@ -111,9 +155,7 @@
<fsummary>Creates a dynamic web page and returns it as a list.
This function is deprecated and is only kept for backwards compatibility.</fsummary>
<type>
- <v>Env = [EnvironmentDirectives] ++ ParsedHeader</v>
- <v>EnvironmentDirectives = {Key,Value}</v>
- <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name.</v>
+ <v>Env = env()</v>
<v>Input = string()</v>
<v>Response = string()</v>
</type>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index b3d2221930..5cebce18a9 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,40 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.2.1</title>
+ <section><title>Inets 6.2.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Put back unused module inets_regexp and remove it in OTP
+ 19 instead as it is an incompatibility, although it is an
+ undocumented module and should not affect other
+ applications.</p>
+ <p>
+ Own Id: OTP-13533</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add environment information item peer_cert to mod_esi</p>
+ <p>
+ Own Id: OTP-13510</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 366e37742b..424d269859 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -20,7 +20,7 @@
%%
-module(httpd_example).
-export([print/1]).
--export([get/2, post/2, yahoo/2, test1/2, get_bin/2]).
+-export([get/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
-export([newformat/3]).
%% These are used by the inets test-suite
@@ -94,10 +94,26 @@ default(Env,Input) ->
io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
footer()].
+peer(Env, Input) ->
+ Header =
+ case proplists:get_value(peer_cert, Env) of
+ undefined ->
+ header("text/html", "Peer-Cert-Exist:false");
+ _ ->
+ header("text/html", "Peer-Cert-Exist:true")
+ end,
+ [Header,
+ top("Test peer_cert environment option"),
+ "<B>Peer cert:</B> ",
+ io_lib:format("~p",[proplists:get_value(peer_cert, Env)]),"\n",
+ footer()].
+
header() ->
header("text/html").
header(MimeType) ->
"Content-type: " ++ MimeType ++ "\r\n\r\n".
+header(MimeType, Other) ->
+ "Content-type: " ++ MimeType ++ "\r\n" ++ Other ++ "\r\n\r\n".
top(Title) ->
"<HTML>
diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl
index 25f9bea7b3..e15613273e 100644
--- a/lib/inets/src/http_server/httpd_script_env.erl
+++ b/lib/inets/src/http_server/httpd_script_env.erl
@@ -61,6 +61,19 @@ which_port(#mod{config_db = ConfigDb}) ->
which_peername(#mod{init_data = #init_data{peername = {_, RemoteAddr}}}) ->
RemoteAddr.
+which_peercert(#mod{socket_type = {Type, _}, socket = Socket}) when Type == essl;
+ Type == ssl ->
+ case ssl:peercert(Socket) of
+ {ok, Cert} ->
+ Cert;
+ {error, no_peercert} ->
+ no_peercert;
+ _ ->
+ undefined
+ end;
+which_peercert(_) -> %% Not an ssl connection
+ undefined.
+
which_resolve(#mod{init_data = #init_data{resolve = Resolve}}) ->
Resolve.
@@ -78,6 +91,7 @@ create_basic_elements(esi, ModData) ->
{server_port, which_port(ModData)},
{request_method, which_method(ModData)},
{remote_addr, which_peername(ModData)},
+ {peer_cert, which_peercert(ModData)},
{script_name, which_request_uri(ModData)}];
create_basic_elements(cgi, ModData) ->
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index 1d870c14e8..a294381f67 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -49,7 +49,8 @@ MODULES = \
inets_sup \
inets_trace \
inets_lib \
- inets_time_compat
+ inets_time_compat \
+ inets_regexp
INTERNAL_HRL_FILES = inets_internal.hrl
EXTERNAL_HRL_FILES = ../../include/httpd.hrl \
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index 5706a335d7..abf8c0cdea 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -29,6 +29,7 @@
inets_trace,
inets_lib,
inets_time_compat,
+ inets_regexp,
%% FTP
ftp,
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 4d0d656acb..3a31daeb20 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,12 +18,10 @@
%% %CopyrightEnd%
{"%VSN%",
[
- {<<"6.2">>, [{load_module, httpc, soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
- {<<"6.2">>, [{load_module, httpc, soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
diff --git a/lib/inets/src/inets_app/inets_regexp.erl b/lib/inets/src/inets_app/inets_regexp.erl
new file mode 100644
index 0000000000..fc1608bc5a
--- /dev/null
+++ b/lib/inets/src/inets_app/inets_regexp.erl
@@ -0,0 +1,414 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(inets_regexp).
+
+-export([parse/1, match/2, first_match/2, split/2, sub/3, gsub/3]).
+
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+%% parse(RegExp) -> {ok, RE} | {error, E}.
+%% Parse the regexp described in the string RegExp.
+
+parse(S) ->
+ case (catch reg(S)) of
+ {R, []} ->
+ {ok, R};
+ {_R, [C|_]} ->
+ {error, {illegal, [C]}};
+ {error, E} ->
+ {error, E}
+ end.
+
+
+%% Find the longest match of RegExp in String.
+
+match(S, RegExp) when is_list(RegExp) ->
+ case parse(RegExp) of
+ {ok,RE} -> match(S, RE);
+ {error,E} -> {error,E}
+ end;
+match(S, RE) ->
+ case match(RE, S, 1, 0, -1) of
+ {Start,Len} when Len >= 0 ->
+ {match, Start, Len};
+ {_Start,_Len} ->
+ nomatch
+ end.
+
+%% Find the first match of RegExp in String.
+
+first_match(S, RegExp) when is_list(RegExp) ->
+ case parse(RegExp) of
+ {ok, RE} ->
+ first_match(S, RE);
+ {error, E} ->
+ {error, E}
+ end;
+first_match(S, RE) ->
+ case first_match(RE, S, 1) of
+ {Start,Len} when Len >= 0 ->
+ {match, Start,Len};
+ nomatch ->
+ nomatch
+ end.
+
+first_match(RE, S, St) when S =/= [] ->
+ case re_apply(S, St, RE) of
+ {match, P, _Rest} ->
+ {St, P-St};
+ nomatch ->
+ first_match(RE, tl(S), St+1)
+ end;
+first_match(_RE, [], _St) ->
+ nomatch.
+
+
+match(RE, S, St, Pos, L) ->
+ case first_match(RE, S, St) of
+ {St1, L1} ->
+ Nst = St1 + 1,
+ if L1 > L ->
+ match(RE, lists:nthtail(Nst-St, S), Nst, St1, L1);
+ true ->
+ match(RE, lists:nthtail(Nst-St, S), Nst, Pos, L)
+ end;
+ nomatch ->
+ {Pos, L}
+ end.
+
+
+%% Split a string into substrings where the RegExp describes the
+%% field seperator. The RegExp " " is specially treated.
+
+split(String, " ") -> %This is really special
+ {ok, RE} = parse("[ \t]+"),
+ case split_apply(String, RE, true) of
+ [[]|Ss] ->
+ {ok,Ss};
+ Ss ->
+ {ok,Ss}
+ end;
+split(String, RegExp) when is_list(RegExp) ->
+ case parse(RegExp) of
+ {ok, RE} ->
+ {ok, split_apply(String, RE, false)};
+ {error, E} ->
+ {error,E}
+ end;
+split(String, RE) ->
+ {ok, split_apply(String, RE, false)}.
+
+
+%% Substitute the first match of the regular expression RegExp
+%% with the string Replace in String. Accept pre-parsed regular
+%% expressions.
+
+sub(String, RegExp, Rep) when is_list(RegExp) ->
+ case parse(RegExp) of
+ {ok, RE} ->
+ sub(String, RE, Rep);
+ {error, E} ->
+ {error, E}
+ end;
+sub(String, RE, Rep) ->
+ Ss = sub_match(String, RE, 1),
+ {ok, sub_repl(Ss, Rep, String, 1), length(Ss)}.
+
+
+%% Substitute every match of the regular expression RegExp with
+%% the string New in String. Accept pre-parsed regular expressions.
+
+gsub(String, RegExp, Rep) when is_list(RegExp) ->
+ case parse(RegExp) of
+ {ok, RE} ->
+ gsub(String, RE, Rep);
+ {error, E} ->
+ {error, E}
+ end;
+gsub(String, RE, Rep) ->
+ Ss = matches(String, RE, 1),
+ {ok, sub_repl(Ss, Rep, String, 1), length(Ss)}.
+
+
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+
+%% This is the regular expression grammar used. It is equivalent to the
+%% one used in AWK, except that we allow ^ $ to be used anywhere and fail
+%% in the matching.
+%%
+%% reg -> reg1 : '$1'.
+%% reg1 -> reg1 "|" reg2 : {'or','$1','$2'}.
+%% reg1 -> reg2 : '$1'.
+%% reg2 -> reg2 reg3 : {concat,'$1','$2'}.
+%% reg2 -> reg3 : '$1'.
+%% reg3 -> reg3 "*" : {kclosure,'$1'}.
+%% reg3 -> reg3 "+" : {pclosure,'$1'}.
+%% reg3 -> reg3 "?" : {optional,'$1'}.
+%% reg3 -> reg4 : '$1'.
+%% reg4 -> "(" reg ")" : '$2'.
+%% reg4 -> "\\" char : '$2'.
+%% reg4 -> "^" : bos.
+%% reg4 -> "$" : eos.
+%% reg4 -> "." : char.
+%% reg4 -> "[" class "]" : {char_class,char_class('$2')}
+%% reg4 -> "[" "^" class "]" : {comp_class,char_class('$3')}
+%% reg4 -> "\"" chars "\"" : char_string('$2')
+%% reg4 -> char : '$1'.
+%% reg4 -> empty : epsilon.
+%% The grammar of the current regular expressions. The actual parser
+%% is a recursive descent implementation of the grammar.
+
+reg(S) -> reg1(S).
+
+%% reg1 -> reg2 reg1'
+%% reg1' -> "|" reg2
+%% reg1' -> empty
+
+reg1(S0) ->
+ {L,S1} = reg2(S0),
+ reg1p(S1, L).
+
+reg1p([$||S0], L) ->
+ {R,S1} = reg2(S0),
+ reg1p(S1, {'or',L,R});
+reg1p(S, L) -> {L,S}.
+
+%% reg2 -> reg3 reg2'
+%% reg2' -> reg3
+%% reg2' -> empty
+
+reg2(S0) ->
+ {L,S1} = reg3(S0),
+ reg2p(S1, L).
+
+reg2p([C|S0], L) when (C =/= $|) andalso (C =/= $)) ->
+ {R,S1} = reg3([C|S0]),
+ reg2p(S1, {concat,L,R});
+reg2p(S, L) -> {L,S}.
+
+%% reg3 -> reg4 reg3'
+%% reg3' -> "*" reg3'
+%% reg3' -> "+" reg3'
+%% reg3' -> "?" reg3'
+%% reg3' -> empty
+
+reg3(S0) ->
+ {L,S1} = reg4(S0),
+ reg3p(S1, L).
+
+reg3p([$*|S], L) -> reg3p(S, {kclosure,L});
+reg3p([$+|S], L) -> reg3p(S, {pclosure,L});
+reg3p([$?|S], L) -> reg3p(S, {optional,L});
+reg3p(S, L) -> {L,S}.
+
+reg4([$(|S0]) ->
+ case reg(S0) of
+ {R,[$)|S1]} -> {R,S1};
+ {_R,_S} -> throw({error,{unterminated,"("}})
+ end;
+reg4([$\\,O1,O2,O3|S])
+ when ((O1 >= $0) andalso
+ (O1 =< $7) andalso
+ (O2 >= $0) andalso
+ (O2 =< $7) andalso
+ (O3 >= $0) andalso
+ (O3 =< $7)) ->
+ {(O1*8 + O2)*8 + O3 - 73*$0,S};
+reg4([$\\,C|S]) ->
+ {escape_char(C),S};
+reg4([$\\]) ->
+ throw({error, {unterminated,"\\"}});
+reg4([$^|S]) ->
+ {bos,S};
+reg4([$$|S]) ->
+ {eos,S};
+reg4([$.|S]) ->
+ {{comp_class,"\n"},S};
+reg4("[^" ++ S0) ->
+ case char_class(S0) of
+ {Cc,[$]|S1]} -> {{comp_class,Cc},S1};
+ {_Cc,_S} -> throw({error,{unterminated,"["}})
+ end;
+reg4([$[|S0]) ->
+ case char_class(S0) of
+ {Cc,[$]|S1]} -> {{char_class,Cc},S1};
+ {_Cc,_S1} -> throw({error,{unterminated,"["}})
+ end;
+reg4([C|S])
+ when (C =/= $*) andalso (C =/= $+) andalso (C =/= $?) andalso (C =/= $]) ->
+ {C, S};
+reg4([C|_S]) ->
+ throw({error,{illegal,[C]}});
+reg4([]) ->
+ {epsilon,[]}.
+
+escape_char($n) -> $\n; %\n = LF
+escape_char($r) -> $\r; %\r = CR
+escape_char($t) -> $\t; %\t = TAB
+escape_char($v) -> $\v; %\v = VT
+escape_char($b) -> $\b; %\b = BS
+escape_char($f) -> $\f; %\f = FF
+escape_char($e) -> $\e; %\e = ESC
+escape_char($s) -> $\s; %\s = SPACE
+escape_char($d) -> $\d; %\d = DEL
+escape_char(C) -> C.
+
+char_class([$]|S]) -> char_class(S, [$]]);
+char_class(S) -> char_class(S, []).
+
+char($\\, [O1,O2,O3|S]) when
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
+ {(O1*8 + O2)*8 + O3 - 73*$0,S};
+char($\\, [C|S]) -> {escape_char(C),S};
+char(C, S) -> {C,S}.
+
+char_class([C1|S0], Cc) when C1 =/= $] ->
+ case char(C1, S0) of
+ {Cf,[$-,C2|S1]} when C2 =/= $] ->
+ case char(C2, S1) of
+ {Cl,S2} when Cf < Cl -> char_class(S2, [{Cf,Cl}|Cc]);
+ {Cl,_S2} -> throw({error,{char_class,[Cf,$-,Cl]}})
+ end;
+ {C,S1} -> char_class(S1, [C|Cc])
+ end;
+char_class(S, Cc) -> {Cc,S}.
+
+
+%% re_apply(String, StartPos, RegExp) -> re_app_res().
+%%
+%% Apply the (parse of the) regular expression RegExp to String. If
+%% there is a match return the position of the remaining string and
+%% the string if else return 'nomatch'. BestMatch specifies if we want
+%% the longest match, or just a match.
+%%
+%% StartPos should be the real start position as it is used to decide
+%% if we ae at the beginning of the string.
+%%
+%% Pass two functions to re_apply_or so it can decide, on the basis
+%% of BestMatch, whether to just any take any match or try both to
+%% find the longest. This is slower but saves duplicatng code.
+
+re_apply(S, St, RE) -> re_apply(RE, [], S, St).
+
+re_apply(epsilon, More, S, P) -> %This always matches
+ re_apply_more(More, S, P);
+re_apply({'or',RE1,RE2}, More, S, P) ->
+ re_apply_or(re_apply(RE1, More, S, P),
+ re_apply(RE2, More, S, P));
+re_apply({concat,RE1,RE2}, More, S0, P) ->
+ re_apply(RE1, [RE2|More], S0, P);
+re_apply({kclosure,CE}, More, S, P) ->
+ %% Be careful with the recursion, explicitly do one call before
+ %% looping.
+ re_apply_or(re_apply_more(More, S, P),
+ re_apply(CE, [{kclosure,CE}|More], S, P));
+re_apply({pclosure,CE}, More, S, P) ->
+ re_apply(CE, [{kclosure,CE}|More], S, P);
+re_apply({optional,CE}, More, S, P) ->
+ re_apply_or(re_apply_more(More, S, P),
+ re_apply(CE, More, S, P));
+re_apply(bos, More, S, 1) -> re_apply_more(More, S, 1);
+re_apply(eos, More, [$\n|S], P) -> re_apply_more(More, S, P);
+re_apply(eos, More, [], P) -> re_apply_more(More, [], P);
+re_apply({char_class,Cc}, More, [C|S], P) ->
+ case in_char_class(C, Cc) of
+ true -> re_apply_more(More, S, P+1);
+ false -> nomatch
+ end;
+re_apply({comp_class,Cc}, More, [C|S], P) ->
+ case in_char_class(C, Cc) of
+ true -> nomatch;
+ false -> re_apply_more(More, S, P+1)
+ end;
+re_apply(C, More, [C|S], P) when is_integer(C) ->
+ re_apply_more(More, S, P+1);
+re_apply(_RE, _More, _S, _P) -> nomatch.
+
+%% re_apply_more([RegExp], String, Length) -> re_app_res().
+
+re_apply_more([RE|More], S, P) -> re_apply(RE, More, S, P);
+re_apply_more([], S, P) -> {match,P,S}.
+
+%% in_char_class(Char, Class) -> bool().
+
+in_char_class(C, [{C1,C2}|_Cc]) when C >= C1, C =< C2 -> true;
+in_char_class(C, [C|_Cc]) -> true;
+in_char_class(C, [_|Cc]) -> in_char_class(C, Cc);
+in_char_class(_C, []) -> false.
+
+%% re_apply_or(Match1, Match2) -> re_app_res().
+%% If we want the best match then choose the longest match, else just
+%% choose one by trying sequentially.
+
+re_apply_or({match,P1,S1}, {match,P2,_S2}) when P1 >= P2 -> {match,P1,S1};
+re_apply_or({match,_P1,_S1}, {match,P2,S2}) -> {match,P2,S2};
+re_apply_or(nomatch, R2) -> R2;
+re_apply_or(R1, nomatch) -> R1.
+
+
+matches(S, RE, St) ->
+ case first_match(RE, S, St) of
+ {St1,0} ->
+ [{St1,0}|matches(string:substr(S, St1+2-St), RE, St1+1)];
+ {St1,L1} ->
+ [{St1,L1}|matches(string:substr(S, St1+L1+1-St), RE, St1+L1)];
+ nomatch ->
+ []
+ end.
+
+sub_match(S, RE, St) ->
+ case first_match(RE, S, St) of
+ {St1,L1} -> [{St1,L1}];
+ nomatch -> []
+ end.
+
+sub_repl([{St,L}|Ss], Rep, S, Pos) ->
+ Rs = sub_repl(Ss, Rep, S, St+L),
+ string:substr(S, Pos, St-Pos) ++
+ sub_repl(Rep, string:substr(S, St, L), Rs);
+sub_repl([], _Rep, S, Pos) ->
+ string:substr(S, Pos).
+
+sub_repl([$&|Rep], M, Rest) -> M ++ sub_repl(Rep, M, Rest);
+sub_repl("\\&" ++ Rep, M, Rest) -> [$&|sub_repl(Rep, M, Rest)];
+sub_repl([C|Rep], M, Rest) -> [C|sub_repl(Rep, M, Rest)];
+sub_repl([], _M, Rest) -> Rest.
+
+split_apply(S, RE, Trim) -> split_apply(S, 1, RE, Trim, []).
+
+split_apply([], _P, _RE, true, []) ->
+ [];
+split_apply([], _P, _RE, _T, Sub) ->
+ [lists:reverse(Sub)];
+split_apply(S, P, RE, T, Sub) ->
+ case re_apply(S, P, RE) of
+ {match,P,_Rest} ->
+ split_apply(tl(S), P+1, RE, T, [hd(S)|Sub]);
+ {match,P1,Rest} ->
+ [lists:reverse(Sub)|split_apply(Rest, P1, RE, T, [])];
+ nomatch ->
+ split_apply(tl(S), P+1, RE, T, [hd(S)|Sub])
+ end.
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 1d8a603981..93520c1cb4 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -755,7 +755,11 @@ esi(Config) when is_list(Config) ->
%% Check "ErlScriptNoCache" directive (default: false)
ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
Config, [{statuscode, 200},
- {no_header, "cache-control"}]).
+ {no_header, "cache-control"}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:peer ",
+ Config, [{statuscode, 200},
+ {header, "peer-cert-exist", peer(Config)}]).
+
%%-------------------------------------------------------------------------
mod_esi_chunk_timeout(Config) when is_list(Config) ->
ok = httpd_1_1:mod_esi_chunk_timeout(?config(type, Config),
@@ -2065,3 +2069,11 @@ response_default_headers() ->
{"X-Frame-Options", "SAMEORIGIN"},
%% Override built-in default
{"Date", "Override-date"}].
+
+peer(Config) ->
+ case proplists:get_value(type, Config) of
+ ssl ->
+ "true";
+ _ ->
+ "false"
+ end. \ No newline at end of file
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 121f55c389..543e0d44fd 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.2.1
+INETS_VSN = 6.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/java_src/pom.xml.src b/lib/jinterface/java_src/pom.xml.src
index cef49b735a..98232db78c 100644
--- a/lib/jinterface/java_src/pom.xml.src
+++ b/lib/jinterface/java_src/pom.xml.src
@@ -7,14 +7,14 @@
<version>%VSN%</version>
<name>jinterface</name>
<description>
- Jinterface Java package contains java classes, which help you integrate programs written in Java with Erlang.
+ Jinterface Java package contains java classes, which help you integrate programs written in Java with Erlang.
Erlang is a programming language designed at the Ericsson Computer Science Laboratory.
- </description>
+ </description>
<url>http://erlang.org/</url>
<licenses>
<license>
- <name>ERLANG PUBLIC LICENSE 1.1</name>
- <url>http://www.erlang.org/EPLICENSE</url>
+ <name>Apache License 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
@@ -37,14 +37,16 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+ <version>3.5.1</version>
<configuration>
- <source>1.5</source>
- <target>1.5</target>
+ <source>1.6</source>
+ <target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.10</version>
<executions>
<execution>
<phase>generate-sources</phase>
@@ -59,6 +61,32 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>3.0.0</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.10.3</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<distributionManagement>
@@ -85,7 +113,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
- <version>1.0-alpha-4</version>
+ <version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 25dddb1f6c..6174136507 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -32,7 +32,12 @@
-import(lists, [foreach/2]).
--type on_load_item() :: {reference(),module(),file:name_all(),[pid()]}.
+-type on_load_action() ::
+ fun((term(), state()) -> {'reply',term(),state()} |
+ {'noreply',state()}).
+
+-type on_load_item() :: {{pid(),reference()},module(),
+ [{pid(),on_load_action()}]}.
-record(state, {supervisor :: pid(),
root :: file:name_all(),
@@ -76,7 +81,7 @@ init(Ref, Parent, [Root,Mode]) ->
interactive ->
LibDir = filename:append(Root, "lib"),
{ok,Dirs} = erl_prim_loader:list_dir(LibDir),
- {Paths,_Libs} = make_path(LibDir, Dirs),
+ Paths = make_path(LibDir, Dirs),
UserLibPaths = get_user_lib_dirs(),
["."] ++ UserLibPaths ++ Paths;
_ ->
@@ -111,7 +116,7 @@ get_user_lib_dirs() ->
get_user_lib_dirs_1([Dir|DirList]) ->
case erl_prim_loader:list_dir(Dir) of
{ok, Dirs} ->
- {Paths,_Libs} = make_path(Dir, Dirs),
+ Paths = make_path(Dir, Dirs),
%% Only add paths trailing with ./ebin.
[P || P <- Paths, filename:basename(P) =:= "ebin"] ++
get_user_lib_dirs_1(DirList);
@@ -142,7 +147,7 @@ reply(Pid, Res) ->
loop(#state{supervisor=Supervisor}=State0) ->
receive
{code_call, Pid, Req} ->
- case handle_call(Req, {Pid, call}, State0) of
+ case handle_call(Req, Pid, State0) of
{reply, Res, State} ->
_ = reply(Pid, Res),
loop(State);
@@ -155,8 +160,8 @@ loop(#state{supervisor=Supervisor}=State0) ->
system_terminate(Reason, Supervisor, [], State0);
{system, From, Msg} ->
handle_system_msg(running,Msg, From, Supervisor, State0);
- {'DOWN',Ref,process,_,Res} ->
- State = finish_on_load(Ref, Res, State0),
+ {'DOWN',Ref,process,Pid,Res} ->
+ State = finish_on_load({Pid,Ref}, Res, State0),
loop(State);
_Msg ->
loop(State0)
@@ -225,90 +230,90 @@ system_code_change(State, _Module, _OldVsn, _Extra) ->
%% The gen_server call back functions.
%%
-handle_call({stick_dir,Dir}, {_From,_Tag}, S) ->
+handle_call({stick_dir,Dir}, _From, S) ->
{reply,stick_dir(Dir, true, S),S};
-handle_call({unstick_dir,Dir}, {_From,_Tag}, S) ->
+handle_call({unstick_dir,Dir}, _From, S) ->
{reply,stick_dir(Dir, false, S),S};
-handle_call({stick_mod,Mod}, {_From,_Tag}, S) ->
+handle_call({stick_mod,Mod}, _From, S) ->
{reply,stick_mod(Mod, true, S),S};
-handle_call({unstick_mod,Mod}, {_From,_Tag}, S) ->
+handle_call({unstick_mod,Mod}, _From, S) ->
{reply,stick_mod(Mod, false, S),S};
-handle_call({dir,Dir}, {_From,_Tag}, S) ->
+handle_call({dir,Dir}, _From, S) ->
Root = S#state.root,
Resp = do_dir(Root,Dir,S#state.namedb),
{reply,Resp,S};
-handle_call({load_file,Mod}, Caller, St) when is_atom(Mod) ->
- load_file(Mod, Caller, St);
+handle_call({load_file,Mod}, From, St) when is_atom(Mod) ->
+ load_file(Mod, From, St);
-handle_call({add_path,Where,Dir0}, {_From,_Tag},
+handle_call({add_path,Where,Dir0}, _From,
#state{namedb=Namedb,path=Path0}=S) ->
{Resp,Path} = add_path(Where, Dir0, Path0, Namedb),
{reply,Resp,S#state{path=Path}};
-handle_call({add_paths,Where,Dirs0}, {_From,_Tag},
+handle_call({add_paths,Where,Dirs0}, _From,
#state{namedb=Namedb,path=Path0}=S) ->
{Resp,Path} = add_paths(Where, Dirs0, Path0, Namedb),
{reply,Resp,S#state{path=Path}};
-handle_call({set_path,PathList}, {_From,_Tag},
+handle_call({set_path,PathList}, _From,
#state{path=Path0,namedb=Namedb}=S) ->
{Resp,Path,NewDb} = set_path(PathList, Path0, Namedb),
{reply,Resp,S#state{path=Path,namedb=NewDb}};
-handle_call({del_path,Name}, {_From,_Tag},
+handle_call({del_path,Name}, _From,
#state{path=Path0,namedb=Namedb}=S) ->
{Resp,Path} = del_path(Name, Path0, Namedb),
{reply,Resp,S#state{path=Path}};
-handle_call({replace_path,Name,Dir}, {_From,_Tag},
+handle_call({replace_path,Name,Dir}, _From,
#state{path=Path0,namedb=Namedb}=S) ->
{Resp,Path} = replace_path(Name, Dir, Path0, Namedb),
{reply,Resp,S#state{path=Path}};
-handle_call(get_path, {_From,_Tag}, S) ->
+handle_call(get_path, _From, S) ->
{reply,S#state.path,S};
%% Messages to load, delete and purge modules/files.
-handle_call({load_abs,File,Mod}, Caller, S) when is_atom(Mod) ->
+handle_call({load_abs,File,Mod}, From, S) when is_atom(Mod) ->
case modp(File) of
false ->
{reply,{error,badarg},S};
true ->
- load_abs(File, Mod, Caller, S)
+ load_abs(File, Mod, From, S)
end;
-handle_call({load_binary,Mod,File,Bin}, Caller, S) when is_atom(Mod) ->
- do_load_binary(Mod, File, Bin, Caller, S);
+handle_call({load_binary,Mod,File,Bin}, From, S) when is_atom(Mod) ->
+ do_load_binary(Mod, File, Bin, From, S);
-handle_call({load_native_partial,Mod,Bin}, {_From,_Tag}, S) ->
+handle_call({load_native_partial,Mod,Bin}, _From, S) ->
Architecture = erlang:system_info(hipe_architecture),
Result = (catch hipe_unified_loader:load(Mod, Bin, Architecture)),
Status = hipe_result_to_status(Result, S),
{reply,Status,S};
-handle_call({load_native_sticky,Mod,Bin,WholeModule}, {_From,_Tag}, S) ->
+handle_call({load_native_sticky,Mod,Bin,WholeModule}, _From, S) ->
Architecture = erlang:system_info(hipe_architecture),
Result = (catch hipe_unified_loader:load_module(Mod, Bin, WholeModule,
Architecture)),
Status = hipe_result_to_status(Result, S),
{reply,Status,S};
-handle_call({ensure_loaded,Mod}, Caller, St) when is_atom(Mod) ->
+handle_call({ensure_loaded,Mod}, From, St) when is_atom(Mod) ->
case erlang:module_loaded(Mod) of
true ->
{reply,{module,Mod},St};
false when St#state.mode =:= interactive ->
- load_file(Mod, Caller, St);
+ ensure_loaded(Mod, From, St);
false ->
{reply,{error,embedded},St}
end;
-handle_call({delete,Mod}, {_From,_Tag}, St) when is_atom(Mod) ->
+handle_call({delete,Mod}, _From, St) when is_atom(Mod) ->
case catch erlang:delete_module(Mod) of
true ->
ets:delete(St#state.moddb, Mod),
@@ -317,48 +322,50 @@ handle_call({delete,Mod}, {_From,_Tag}, St) when is_atom(Mod) ->
{reply,false,St}
end;
-handle_call({purge,Mod}, {_From,_Tag}, St) when is_atom(Mod) ->
+handle_call({purge,Mod}, _From, St) when is_atom(Mod) ->
{reply,do_purge(Mod),St};
-handle_call({soft_purge,Mod}, {_From,_Tag}, St) when is_atom(Mod) ->
+handle_call({soft_purge,Mod}, _From, St) when is_atom(Mod) ->
{reply,do_soft_purge(Mod),St};
-handle_call({is_loaded,Mod}, {_From,_Tag}, St) when is_atom(Mod) ->
+handle_call({is_loaded,Mod}, _From, St) when is_atom(Mod) ->
{reply,is_loaded(Mod, St#state.moddb),St};
-handle_call(all_loaded, {_From,_Tag}, S) ->
+handle_call(all_loaded, _From, S) ->
Db = S#state.moddb,
{reply,all_loaded(Db),S};
-handle_call({get_object_code,Mod}, {_From,_Tag}, St) when is_atom(Mod) ->
+handle_call({get_object_code,Mod}, _From, St) when is_atom(Mod) ->
Path = St#state.path,
case mod_to_bin(Path, Mod) of
{_,Bin,FName} -> {reply,{Mod,Bin,FName},St};
Error -> {reply,Error,St}
end;
-handle_call({is_sticky, Mod}, {_From,_Tag}, S) ->
+handle_call({is_sticky, Mod}, _From, S) ->
Db = S#state.moddb,
{reply, is_sticky(Mod,Db), S};
-handle_call(stop,{_From,_Tag}, S) ->
+handle_call(stop,_From, S) ->
{stop,normal,stopped,S};
-handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}, {_From,_Tag}, S=#state{mode=Mode}) ->
- case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) of
+handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun},
+ _From, S=#state{mode=Mode}) ->
+ case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo,
+ ParserFun) of
{ok, Files} ->
{reply, {ok, Mode, Files}, S};
{error, _Reason} = Error ->
{reply, Error, S}
end;
-handle_call(get_mode, {_From,_Tag}, S=#state{mode=Mode}) ->
+handle_call(get_mode, _From, S=#state{mode=Mode}) ->
{reply, Mode, S};
-handle_call({finish_loading,Prepared,EnsureLoaded}, {_,_}, S) ->
+handle_call({finish_loading,Prepared,EnsureLoaded}, _From, S) ->
{reply,finish_loading(Prepared, EnsureLoaded, S),S};
-handle_call(Other,{_From,_Tag}, S) ->
+handle_call(Other,_From, S) ->
error_msg(" ** Codeserver*** ignoring ~w~n ",[Other]),
{noreply,S}.
@@ -371,7 +378,7 @@ handle_call(Other,{_From,_Tag}, S) ->
%%
make_path(BundleDir, Bundles0) ->
Bundles = choose_bundles(Bundles0),
- make_path(BundleDir, Bundles, [], []).
+ make_path(BundleDir, Bundles, []).
choose_bundles(Bundles) ->
ArchiveExt = archive_extension(),
@@ -381,12 +388,10 @@ choose_bundles(Bundles) ->
create_bundle(FullName, ArchiveExt) ->
BaseName = filename:basename(FullName, ArchiveExt),
- case split(BaseName, "-") of
- [_, _|_] = Toks ->
- VsnStr = lists:last(Toks),
+ case split_base(BaseName) of
+ {Name, VsnStr} ->
case vsn_to_num(VsnStr) of
{ok, VsnNum} ->
- Name = join(lists:sublist(Toks, length(Toks)-1),"-"),
{Name,VsnNum,FullName};
false ->
{FullName,[0],FullName}
@@ -457,41 +462,44 @@ choose([{Name,NumVsn,NewFullName}=New|Bs], Acc, ArchiveExt) ->
choose([],Acc, _ArchiveExt) ->
Acc.
-make_path(_,[],Res,Bs) ->
- {Res,Bs};
-make_path(BundleDir,[Bundle|Tail],Res,Bs) ->
- Dir = filename:append(BundleDir,Bundle),
- Ebin = filename:append(Dir,"ebin"),
+make_path(_, [], Res) ->
+ Res;
+make_path(BundleDir, [Bundle|Tail], Res) ->
+ Dir = filename:append(BundleDir, Bundle),
+ Ebin = filename:append(Dir, "ebin"),
%% First try with /ebin
- case erl_prim_loader:read_file_info(Ebin) of
- {ok,#file_info{type=directory}} ->
- make_path(BundleDir,Tail,[Ebin|Res],[Bundle|Bs]);
- _ ->
+ case is_dir(Ebin) of
+ true ->
+ make_path(BundleDir, Tail, [Ebin|Res]);
+ false ->
%% Second try with archive
Ext = archive_extension(),
- Base = filename:basename(Dir, Ext),
- Ebin2 = filename:join([filename:dirname(Dir), Base ++ Ext, Base, "ebin"]),
+ Base = filename:basename(Bundle, Ext),
+ Ebin2 = filename:join([BundleDir, Base ++ Ext, Base, "ebin"]),
Ebins =
- case split(Base, "-") of
- [_, _|_] = Toks ->
- AppName = join(lists:sublist(Toks, length(Toks)-1),"-"),
- Ebin3 = filename:join([filename:dirname(Dir), Base ++ Ext, AppName, "ebin"]),
+ case split_base(Base) of
+ {AppName,_} ->
+ Ebin3 = filename:join([BundleDir, Base ++ Ext,
+ AppName, "ebin"]),
[Ebin3, Ebin2, Dir];
_ ->
[Ebin2, Dir]
end,
- try_ebin_dirs(Ebins,BundleDir,Tail,Res,Bundle, Bs)
+ case try_ebin_dirs(Ebins) of
+ {ok,FoundEbin} ->
+ make_path(BundleDir, Tail, [FoundEbin|Res]);
+ error ->
+ make_path(BundleDir, Tail, Res)
+ end
end.
-try_ebin_dirs([Ebin | Ebins],BundleDir,Tail,Res,Bundle,Bs) ->
- case erl_prim_loader:read_file_info(Ebin) of
- {ok,#file_info{type=directory}} ->
- make_path(BundleDir,Tail,[Ebin|Res],[Bundle|Bs]);
- _ ->
- try_ebin_dirs(Ebins,BundleDir,Tail,Res,Bundle,Bs)
+try_ebin_dirs([Ebin|Ebins]) ->
+ case is_dir(Ebin) of
+ true -> {ok,Ebin};
+ false -> try_ebin_dirs(Ebins)
end;
-try_ebin_dirs([],BundleDir,Tail,Res,_Bundle,Bs) ->
- make_path(BundleDir,Tail,Res,Bs).
+try_ebin_dirs([]) ->
+ error.
%%
@@ -609,19 +617,34 @@ exclude(Dir,Path) ->
%%
%%
get_name(Dir) ->
- get_name2(get_name1(Dir), []).
+ get_name_from_splitted(filename:split(Dir)).
+
+get_name_from_splitted([DirName,"ebin"]) ->
+ discard_after_hyphen(DirName);
+get_name_from_splitted([DirName]) ->
+ discard_after_hyphen(DirName);
+get_name_from_splitted([_|T]) ->
+ get_name_from_splitted(T);
+get_name_from_splitted([]) ->
+ "". %No name.
+
+discard_after_hyphen("-"++_) ->
+ [];
+discard_after_hyphen([H|T]) ->
+ [H|discard_after_hyphen(T)];
+discard_after_hyphen([]) ->
+ [].
-get_name1(Dir) ->
- case lists:reverse(filename:split(Dir)) of
- ["ebin",DirName|_] -> DirName;
- [DirName|_] -> DirName;
- _ -> "" % No name !
+split_base(BaseName) ->
+ case split(BaseName, "-") of
+ [_, _|_] = Toks ->
+ Vsn = lists:last(Toks),
+ AllButLast = lists:droplast(Toks),
+ {join(AllButLast, "-"),Vsn};
+ [_|_] ->
+ BaseName
end.
-get_name2([$-|_],Acc) -> lists:reverse(Acc);
-get_name2([H|T],Acc) -> get_name2(T,[H|Acc]);
-get_name2(_,Acc) -> lists:reverse(Acc).
-
check_path(Path) ->
PathChoice = init:code_path_choice(),
ArchiveExt = archive_extension(),
@@ -630,23 +653,23 @@ check_path(Path) ->
do_check_path([], _PathChoice, _ArchiveExt, Acc) ->
{ok, lists:reverse(Acc)};
do_check_path([Dir | Tail], PathChoice, ArchiveExt, Acc) ->
- case catch erl_prim_loader:read_file_info(Dir) of
- {ok, #file_info{type=directory}} ->
+ case is_dir(Dir) of
+ true ->
do_check_path(Tail, PathChoice, ArchiveExt, [Dir | Acc]);
- _ when PathChoice =:= strict ->
+ false when PathChoice =:= strict ->
%% Be strict. Only use dir as explicitly stated
{error, bad_directory};
- _ when PathChoice =:= relaxed ->
+ false when PathChoice =:= relaxed ->
%% Be relaxed
case catch lists:reverse(filename:split(Dir)) of
{'EXIT', _} ->
{error, bad_directory};
["ebin", App] ->
Dir2 = filename:join([App ++ ArchiveExt, App, "ebin"]),
- case erl_prim_loader:read_file_info(Dir2) of
- {ok, #file_info{type = directory}} ->
+ case is_dir(Dir2) of
+ true ->
do_check_path(Tail, PathChoice, ArchiveExt, [Dir2 | Acc]);
- _ ->
+ false ->
{error, bad_directory}
end;
["ebin", App, OptArchive | RevTop] ->
@@ -666,10 +689,10 @@ do_check_path([Dir | Tail], PathChoice, ArchiveExt, Acc) ->
Top = lists:reverse([OptArchive | RevTop]),
filename:join(Top ++ [App ++ ArchiveExt, App, "ebin"])
end,
- case erl_prim_loader:read_file_info(Dir2) of
- {ok, #file_info{type = directory}} ->
+ case is_dir(Dir2) of
+ true ->
do_check_path(Tail, PathChoice, ArchiveExt, [Dir2 | Acc]);
- _ ->
+ false ->
{error, bad_directory}
end;
_ ->
@@ -768,7 +791,7 @@ init_namedb(Path) ->
Db.
init_namedb([P|Path], Db) ->
- insert_name(P, Db),
+ insert_dir(P, Db),
init_namedb(Path, Db);
init_namedb([], _) ->
ok.
@@ -781,59 +804,39 @@ clear_namedb([], _) ->
ok.
-endif.
-insert_name(Dir, Db) ->
- case get_name(Dir) of
- Dir -> false;
- Name -> insert_name(Name, Dir, Db)
- end.
+%% Dir must be a complete pathname (not only a name).
+insert_dir(Dir, Db) ->
+ Splitted = filename:split(Dir),
+ Name = get_name_from_splitted(Splitted),
+ AppDir = filename:join(del_ebin_1(Splitted)),
+ do_insert_name(Name, AppDir, Db).
insert_name(Name, Dir, Db) ->
AppDir = del_ebin(Dir),
+ do_insert_name(Name, AppDir, Db).
+
+do_insert_name(Name, AppDir, Db) ->
{Base, SubDirs} = archive_subdirs(AppDir),
ets:insert(Db, {Name, AppDir, Base, SubDirs}),
true.
archive_subdirs(AppDir) ->
- IsDir =
- fun(RelFile) ->
- File = filename:join([AppDir, RelFile]),
- case erl_prim_loader:read_file_info(File) of
- {ok, #file_info{type = directory}} ->
- false;
- _ ->
- true
- end
- end,
- {Base, ArchiveDirs} = all_archive_subdirs(AppDir),
- {Base, lists:filter(IsDir, ArchiveDirs)}.
-
-all_archive_subdirs(AppDir) ->
- Ext = archive_extension(),
Base = filename:basename(AppDir),
- Dirs =
- case split(Base, "-") of
- [_, _|_] = Toks ->
- Base2 = join(lists:sublist(Toks, length(Toks)-1), "-"),
- [Base2, Base];
- _ ->
- [Base]
+ Dirs = case split_base(Base) of
+ {Name, _} -> [Name, Base];
+ _ -> [Base]
end,
+ Ext = archive_extension(),
try_archive_subdirs(AppDir ++ Ext, Base, Dirs).
try_archive_subdirs(Archive, Base, [Dir | Dirs]) ->
- ArchiveDir = filename:join([Archive, Dir]),
+ ArchiveDir = filename:append(Archive, Dir),
case erl_prim_loader:list_dir(ArchiveDir) of
{ok, Files} ->
- IsDir =
- fun(RelFile) ->
- File = filename:join([ArchiveDir, RelFile]),
- case erl_prim_loader:read_file_info(File) of
- {ok, #file_info{type = directory}} ->
- true;
- _ ->
- false
- end
- end,
+ IsDir = fun(RelFile) ->
+ File = filename:append(ArchiveDir, RelFile),
+ is_dir(File)
+ end,
{Dir, lists:filter(IsDir, Files)};
_ ->
try_archive_subdirs(Archive, Base, Dirs)
@@ -927,22 +930,22 @@ check_pars(Name,Dir) ->
end.
del_ebin(Dir) ->
- case filename:basename(Dir) of
- "ebin" ->
- Dir2 = filename:dirname(Dir),
- Dir3 = filename:dirname(Dir2),
- Ext = archive_extension(),
- case filename:extension(Dir3) of
- E when E =:= Ext ->
- %% Strip archive extension
- filename:join([filename:dirname(Dir3),
- filename:basename(Dir3, Ext)]);
- _ ->
- Dir2
- end;
- _ ->
- Dir
- end.
+ filename:join(del_ebin_1(filename:split(Dir))).
+
+del_ebin_1([Parent,App,"ebin"]) ->
+ Ext = archive_extension(),
+ case filename:basename(Parent, Ext) of
+ Parent ->
+ %% Plain directory.
+ [Parent,App];
+ Archive ->
+ %% Archive.
+ [Archive]
+ end;
+del_ebin_1([H|T]) ->
+ [H|del_ebin_1(T)];
+del_ebin_1([]) ->
+ [].
replace_name(Dir, Db) ->
case get_name(Dir) of
@@ -1058,14 +1061,14 @@ add_paths(Where,[Dir|Tail],Path,NameDb) ->
add_paths(_,_,Path,_) ->
{ok,Path}.
-do_load_binary(Module, File, Binary, Caller, St) ->
+do_load_binary(Module, File, Binary, From, St) ->
case modp(File) andalso is_binary(Binary) of
true ->
case erlang:module_loaded(Module) of
true -> do_purge(Module);
false -> ok
end,
- try_load_module(File, Module, Binary, Caller, St);
+ try_load_module(File, Module, Binary, From, St);
false ->
{reply,{error,badarg},St}
end.
@@ -1074,63 +1077,61 @@ modp(Atom) when is_atom(Atom) -> true;
modp(List) when is_list(List) -> int_list(List);
modp(_) -> false.
-load_abs(File, Mod, Caller, St) ->
+load_abs(File, Mod, From, St) ->
Ext = objfile_extension(),
FileName0 = lists:concat([File, Ext]),
FileName = absname(FileName0),
case erl_prim_loader:get_file(FileName) of
{ok,Bin,_} ->
- try_load_module(FileName, Mod, Bin, Caller, St);
+ try_load_module(FileName, Mod, Bin, From, St);
error ->
{reply,{error,nofile},St}
end.
-try_load_module(File, Mod, Bin, {From,_}=Caller, St0) ->
- case pending_on_load(Mod, From, St0) of
- no ->
- try_load_module_1(File, Mod, Bin, Caller, St0);
- {yes,St} ->
- {noreply,St}
- end.
+try_load_module(File, Mod, Bin, From, St) ->
+ Action = fun(_, S) ->
+ try_load_module_1(File, Mod, Bin, From, S)
+ end,
+ handle_pending_on_load(Action, Mod, From, St).
-try_load_module_1(File, Mod, Bin, Caller, #state{moddb=Db}=St) ->
+try_load_module_1(File, Mod, Bin, From, #state{moddb=Db}=St) ->
case is_sticky(Mod, Db) of
true -> %% Sticky file reject the load
error_msg("Can't load module '~w' that resides in sticky dir\n",[Mod]),
{reply,{error,sticky_directory},St};
false ->
Architecture = erlang:system_info(hipe_architecture),
- try_load_module_2(File, Mod, Bin, Caller, Architecture, St)
+ try_load_module_2(File, Mod, Bin, From, Architecture, St)
end.
-try_load_module_2(File, Mod, Bin, Caller, undefined, St) ->
- try_load_module_3(File, Mod, Bin, Caller, undefined, St);
-try_load_module_2(File, Mod, Bin, Caller, Architecture,
+try_load_module_2(File, Mod, Bin, From, undefined, St) ->
+ try_load_module_3(File, Mod, Bin, From, undefined, St);
+try_load_module_2(File, Mod, Bin, From, Architecture,
#state{moddb=Db}=St) ->
case catch hipe_unified_loader:load_native_code(Mod, Bin, Architecture) of
{module,Mod} = Module ->
ets:insert(Db, [{{native,Mod},true},{Mod,File}]),
{reply,Module,St};
no_native ->
- try_load_module_3(File, Mod, Bin, Caller, Architecture, St);
+ try_load_module_3(File, Mod, Bin, From, Architecture, St);
Error ->
error_msg("Native loading of ~ts failed: ~p\n", [File,Error]),
{reply,ok,St}
end.
-try_load_module_3(File, Mod, Bin, Caller, Architecture,
- #state{moddb=Db}=St) ->
- case erlang:load_module(Mod, Bin) of
- {module,Mod} = Module ->
- ets:insert(Db, {Mod,File}),
- post_beam_load([Mod], Architecture, St),
- {reply,Module,St};
- {error,on_load} ->
- handle_on_load(Mod, File, Caller, St);
- {error,What} = Error ->
- error_msg("Loading of ~ts failed: ~p\n", [File, What]),
- {reply,Error,St}
- end.
+try_load_module_3(File, Mod, Bin, From, Architecture, St0) ->
+ Action = fun({module,_}=Module, #state{moddb=Db}=S) ->
+ ets:insert(Db, {Mod,File}),
+ post_beam_load([Mod], Architecture, S),
+ {reply,Module,S};
+ ({error,on_load_failure}=Error, S) ->
+ {reply,Error,S};
+ ({error,What}=Error, S) ->
+ error_msg("Loading of ~ts failed: ~p\n", [File, What]),
+ {reply,Error,S}
+ end,
+ Res = erlang:load_module(Mod, Bin),
+ handle_on_load(Res, Action, Mod, From, St0).
hipe_result_to_status(Result, #state{moddb=Db}) ->
case Result of
@@ -1155,18 +1156,29 @@ int_list([H|T]) when is_integer(H) -> int_list(T);
int_list([_|_]) -> false;
int_list([]) -> true.
-load_file(Mod, {From,_}=Caller, St0) ->
- case pending_on_load(Mod, From, St0) of
- no -> load_file_1(Mod, Caller, St0);
- {yes,St} -> {noreply,St}
- end.
-
-load_file_1(Mod, Caller, #state{path=Path}=St) ->
+ensure_loaded(Mod, From, St0) ->
+ Action = fun(_, S) ->
+ case erlang:module_loaded(Mod) of
+ true ->
+ {reply,{module,Mod},S};
+ false ->
+ load_file_1(Mod, From, S)
+ end
+ end,
+ handle_pending_on_load(Action, Mod, From, St0).
+
+load_file(Mod, From, St0) ->
+ Action = fun(_, S) ->
+ load_file_1(Mod, From, S)
+ end,
+ handle_pending_on_load(Action, Mod, From, St0).
+
+load_file_1(Mod, From, #state{path=Path}=St) ->
case mod_to_bin(Path, Mod) of
error ->
{reply,{error,nofile},St};
{Mod,Binary,File} ->
- try_load_module_1(File, Mod, Binary, Caller, St)
+ try_load_module_1(File, Mod, Binary, From, St)
end.
mod_to_bin([Dir|Tail], Mod) ->
@@ -1174,8 +1186,13 @@ mod_to_bin([Dir|Tail], Mod) ->
case erl_prim_loader:get_file(File) of
error ->
mod_to_bin(Tail, Mod);
- {ok,Bin,FName} ->
- {Mod,Bin,absname(FName)}
+ {ok,Bin,_} ->
+ case filename:pathtype(File) of
+ absolute ->
+ {Mod,Bin,File};
+ _ ->
+ {Mod,Bin,absname(File)}
+ end
end;
mod_to_bin([], Mod) ->
%% At last, try also erl_prim_loader's own method
@@ -1236,6 +1253,11 @@ do_purge(Mod) ->
do_soft_purge(Mod) ->
erts_code_purger:soft_purge(Mod).
+is_dir(Path) ->
+ case erl_prim_loader:read_file_info(Path) of
+ {ok,#file_info{type=directory}} -> true;
+ _ -> false
+ end.
%%%
%%% Loading of multiple modules in parallel.
@@ -1299,59 +1321,78 @@ run([F|Fs], Data0) ->
%% The on_load functionality.
%% -------------------------------------------------------
-handle_on_load(Mod, File, {From,_}, #state{on_load=OnLoad0}=St0) ->
+handle_on_load({error,on_load}, Action, Mod, From, St0) ->
+ #state{on_load=OnLoad0} = St0,
Fun = fun() ->
Res = erlang:call_on_load_function(Mod),
exit(Res)
end,
- {_,Ref} = spawn_monitor(Fun),
- OnLoad = [{Ref,Mod,File,[From]}|OnLoad0],
+ PidRef = spawn_monitor(Fun),
+ PidAction = {From,Action},
+ OnLoad = [{PidRef,Mod,[PidAction]}|OnLoad0],
St = St0#state{on_load=OnLoad},
- {noreply,St}.
+ {noreply,St};
+handle_on_load(Res, Action, _, _, St) ->
+ Action(Res, St).
-pending_on_load(_, _, #state{on_load=[]}) ->
- no;
-pending_on_load(Mod, From, #state{on_load=OnLoad0}=St) ->
- case lists:keymember(Mod, 2, OnLoad0) of
+handle_pending_on_load(Action, Mod, From, #state{on_load=OnLoad0}=St) ->
+ case lists:keyfind(Mod, 2, OnLoad0) of
false ->
- no;
- true ->
- OnLoad = pending_on_load_1(Mod, From, OnLoad0),
- {yes,St#state{on_load=OnLoad}}
+ Action(ok, St);
+ {{From,_Ref},Mod,_Pids} ->
+ %% The on_load function tried to make an external
+ %% call to its own module. That would be a deadlock.
+ %% Fail the call. (The call is probably from error_handler,
+ %% and it will ignore the actual error reason and cause
+ %% an undef execption.)
+ {reply,{error,deadlock},St};
+ {_,_,_} ->
+ OnLoad = handle_pending_on_load_1(Mod, {From,Action}, OnLoad0),
+ {noreply,St#state{on_load=OnLoad}}
end.
-pending_on_load_1(Mod, From, [{Ref,Mod,File,Pids}|T]) ->
- [{Ref,Mod,File,[From|Pids]}|T];
-pending_on_load_1(Mod, From, [H|T]) ->
- [H|pending_on_load_1(Mod, From, T)];
-pending_on_load_1(_, _, []) -> [].
+handle_pending_on_load_1(Mod, From, [{PidRef,Mod,Pids}|T]) ->
+ [{PidRef,Mod,[From|Pids]}|T];
+handle_pending_on_load_1(Mod, From, [H|T]) ->
+ [H|handle_pending_on_load_1(Mod, From, T)];
+handle_pending_on_load_1(_, _, []) -> [].
-finish_on_load(Ref, OnLoadRes, #state{on_load=OnLoad0,moddb=Db}=State) ->
- case lists:keyfind(Ref, 1, OnLoad0) of
+finish_on_load(PidRef, OnLoadRes, #state{on_load=OnLoad0}=St0) ->
+ case lists:keyfind(PidRef, 1, OnLoad0) of
false ->
%% Since this process in general silently ignores messages
%% it doesn't understand, it should also ignore a 'DOWN'
%% message with an unknown reference.
- State;
- {Ref,Mod,File,WaitingPids} ->
- finish_on_load_1(Mod, File, OnLoadRes, WaitingPids, Db),
- OnLoad = [E || {R,_,_,_}=E <- OnLoad0, R =/= Ref],
- State#state{on_load=OnLoad}
+ St0;
+ {PidRef,Mod,Waiting} ->
+ St = finish_on_load_1(Mod, OnLoadRes, Waiting, St0),
+ OnLoad = [E || {R,_,_}=E <- OnLoad0, R =/= PidRef],
+ St#state{on_load=OnLoad}
end.
-finish_on_load_1(Mod, File, OnLoadRes, WaitingPids, Db) ->
+finish_on_load_1(Mod, OnLoadRes, Waiting, St) ->
Keep = OnLoadRes =:= ok,
erlang:finish_after_on_load(Mod, Keep),
Res = case Keep of
false ->
_ = finish_on_load_report(Mod, OnLoadRes),
+ _ = erts_code_purger:purge(Mod),
{error,on_load_failure};
true ->
- ets:insert(Db, {Mod,File}),
{module,Mod}
end,
- _ = [reply(Pid, Res) || Pid <- WaitingPids],
- ok.
+ finish_on_load_2(Waiting, Res, St).
+
+finish_on_load_2([{Pid,Action}|T], Res, St0) ->
+ case Action(Res, St0) of
+ {reply,Rep,St} ->
+ _ = reply(Pid, Rep),
+ finish_on_load_2(T, Res, St);
+ {noreply,St} ->
+ finish_on_load_2(T, Res, St)
+ end;
+finish_on_load_2([], _, St) ->
+ St.
finish_on_load_report(_Mod, Atom) when is_atom(Atom) ->
%% No error reports for atoms.
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index 580a896357..47d0c1b861 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -156,7 +156,7 @@ is_allowed(#hs_data{other_node = Node,
send_status(HSData, not_allowed),
error_msg("** Connection attempt from "
"disallowed node ~w ** ~n", [Node]),
- ?shutdown(Node);
+ ?shutdown2(Node, {is_allowed, not_allowed});
_ -> true
end.
@@ -607,10 +607,10 @@ recv_challenge_reply(#hs_data{socket = Socket,
_ ->
error_msg("** Connection attempt from "
"disallowed node ~w ** ~n", [NodeB]),
- ?shutdown(NodeB)
+ ?shutdown2(NodeB, {recv_challenge_reply_failed, bad_cookie})
end;
- _ ->
- ?shutdown(no_node)
+ Other ->
+ ?shutdown2(no_node, {recv_challenge_reply_failed, Other})
end.
recv_challenge_ack(#hs_data{socket = Socket, f_recv = FRecv,
diff --git a/lib/kernel/src/global_group.erl b/lib/kernel/src/global_group.erl
index e71f83f9d3..8ac0bd9551 100644
--- a/lib/kernel/src/global_group.erl
+++ b/lib/kernel/src/global_group.erl
@@ -692,7 +692,7 @@ handle_cast({registered_names, User}, S) ->
handle_cast({registered_names_res, Result, Pid, From}, S) ->
% io:format(">>>>> registered_names_res Result ~p~n",[Result]),
unlink(Pid),
- exit(Pid, normal),
+ Pid ! kill,
Wait = get(registered_names),
NewWait = lists:delete({Pid, From},Wait),
put(registered_names, NewWait),
@@ -718,7 +718,7 @@ handle_cast({send_res, Result, Name, Msg, Pid, From}, S) ->
ToPid ! Msg
end,
unlink(Pid),
- exit(Pid, normal),
+ Pid ! kill,
Wait = get(send),
NewWait = lists:delete({Pid, From, Name, Msg},Wait),
put(send, NewWait),
@@ -748,7 +748,7 @@ handle_cast({find_name_res, Result, Pid, From}, S) ->
% io:format(">>>>> find_name_res Result ~p~n",[Result]),
% io:format(">>>>> find_name_res get() ~p~n",[get()]),
unlink(Pid),
- exit(Pid, normal),
+ Pid ! kill,
Wait = get(whereis_name),
NewWait = lists:delete({Pid, From},Wait),
put(whereis_name, NewWait),
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index 6f9ce17c0c..21bff02214 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -63,7 +63,7 @@
%%------------------------------------------------------------------------
--type state() :: gb_trees:tree(pid(), {pid(), reference()}).
+-type state() :: map().
%%------------------------------------------------------------------------
@@ -91,7 +91,7 @@ stop(Rpc) ->
init([]) ->
process_flag(trap_exit, true),
- {ok, gb_trees:empty()}.
+ {ok, maps:new()}.
-spec handle_call(term(), term(), state()) ->
{'noreply', state()} |
@@ -130,29 +130,15 @@ handle_cast(_, S) ->
-spec handle_info(term(), state()) -> {'noreply', state()}.
+handle_info({'DOWN', _, process, Caller, normal}, S) ->
+ {noreply, maps:remove(Caller, S)};
handle_info({'DOWN', _, process, Caller, Reason}, S) ->
- case gb_trees:lookup(Caller, S) of
- {value, To} ->
- receive
- {Caller, {reply, Reply}} ->
- gen_server:reply(To, Reply)
- after 0 ->
- gen_server:reply(To, {badrpc, {'EXIT', Reason}})
- end,
- {noreply, gb_trees:delete(Caller, S)};
- none ->
- {noreply, S}
- end;
-handle_info({Caller, {reply, Reply}}, S) ->
- case gb_trees:lookup(Caller, S) of
- {value, To} ->
- receive
- {'DOWN', _, process, Caller, _} ->
- gen_server:reply(To, Reply),
- {noreply, gb_trees:delete(Caller, S)}
- end;
- none ->
- {noreply, S}
+ case maps:get(Caller, S, undefined) of
+ undefined ->
+ {noreply, S};
+ {_, _} = To ->
+ gen_server:reply(To, {badrpc, {'EXIT', Reason}}),
+ {noreply, maps:remove(Caller, S)}
end;
handle_info({From, {sbcast, Name, Msg}}, S) ->
_ = case catch Name ! Msg of %% use catch to get the printout
@@ -190,7 +176,6 @@ code_change(_, S, _) ->
%% Auxiliary function to avoid a false dialyzer warning -- do not inline
%%
handle_call_call(Mod, Fun, Args, Gleader, To, S) ->
- RpcServer = self(),
%% Spawn not to block the rpc server.
{Caller,_} =
erlang:spawn_monitor(
@@ -205,9 +190,9 @@ handle_call_call(Mod, Fun, Args, Gleader, To, S) ->
Result ->
Result
end,
- RpcServer ! {self(), {reply, Reply}}
+ gen_server:reply(To, Reply)
end),
- {noreply, gb_trees:insert(Caller, To, S)}.
+ {noreply, maps:put(Caller, To, S)}.
%% RPC aid functions ....
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 7f9718a354..8da67c89f8 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -20,6 +20,7 @@
-module(code_SUITE).
-include_lib("common_test/include/ct.hrl").
+-include_lib("syntax_tools/include/merl.hrl").
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([set_path/1, get_path/1, add_path/1, add_paths/1, del_path/1,
@@ -33,7 +34,9 @@
where_is_file/1,
purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1,
code_archive/1, code_archive2/1, on_load/1, on_load_binary/1,
- on_load_embedded/1, on_load_errors/1, big_boot_embedded/1,
+ on_load_embedded/1, on_load_errors/1, on_load_update/1,
+ on_load_purge/1, on_load_self_call/1, on_load_pending/1,
+ big_boot_embedded/1,
native_early_modules/1, get_mode/1,
normalized_paths/1]).
@@ -61,7 +64,8 @@ all() ->
ext_mod_dep, clash, where_is_file,
purge_stacktrace, mult_lib_roots,
bad_erl_libs, code_archive, code_archive2, on_load,
- on_load_binary, on_load_embedded, on_load_errors,
+ on_load_binary, on_load_embedded, on_load_errors, on_load_update,
+ on_load_purge, on_load_self_call, on_load_pending,
big_boot_embedded, native_early_modules, get_mode, normalized_paths].
groups() ->
@@ -826,6 +830,12 @@ check_funs({'$M_EXPR',warning_msg,2},
[{code_server,finish_on_load_report,2} | _]) -> 0;
check_funs({'$M_EXPR','$F_EXPR',1},
[{code_server,run,2}|_]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',2},
+ [{code_server,handle_on_load,5}|_]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',2},
+ [{code_server,handle_pending_on_load,4}|_]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',2},
+ [{code_server,finish_on_load_2,3}|_]) -> 0;
%% This is cheating! /raimo
%%
%% check_funs(This = {M,_,_}, Path) ->
@@ -1023,6 +1033,12 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) ->
{ok, _} = zip:create(Archive, [Base],
[{compress, []}, {cwd, PrivDir}]),
+ %% Create a directory and a file outside of the archive.
+ OtherFile = filename:join([RootDir,VsnBase,"other","other.txt"]),
+ OtherContents = ?MODULE:module_info(md5),
+ filelib:ensure_dir(OtherFile),
+ ok = file:write_file(OtherFile, OtherContents),
+
%% Set up ERL_LIBS and start a slave node.
{ok, Node} =
test_server:start_node(code_archive, slave,
@@ -1037,13 +1053,25 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) ->
%% Start the app
ok = rpc:call(Node, application, start, [App]),
+ %% Get the lib dir for the app.
+ AppLibDir = rpc:call(Node, code, lib_dir, [App]),
+ io:format("AppLibDir: ~p\n", [AppLibDir]),
+ AppLibDir = filename:join(RootDir, VsnBase),
+
%% Access the app priv dir
AppPrivDir = rpc:call(Node, code, priv_dir, [App]),
AppPrivFile = filename:join([AppPrivDir, "code_archive.txt"]),
io:format("AppPrivFile: ~p\n", [AppPrivFile]),
- {ok, _Bin, _Path} =
+ {ok, _Bin, _} =
rpc:call(Node, erl_prim_loader, get_file, [AppPrivFile]),
+ %% Read back the other text file.
+ OtherDirPath = rpc:call(Node, code, lib_dir, [App,other]),
+ OtherFilePath = filename:join(OtherDirPath, "other.txt"),
+ io:format("OtherFilePath: ~p\n", [OtherFilePath]),
+ {ok, OtherContents, _} =
+ rpc:call(Node, erl_prim_loader, get_file, [OtherFilePath]),
+
%% Use the app
Tab = code_archive_tab,
Key = foo,
@@ -1166,22 +1194,17 @@ on_load_binary(_) ->
register(Master, self()),
%% Construct, compile and pretty-print.
- Mod = on_load_binary,
+ Mod = ?FUNCTION_NAME,
File = atom_to_list(Mod) ++ ".erl",
- Forms = [{attribute,1,file,{File,1}},
- {attribute,1,module,Mod},
- {attribute,2,export,[{ok,0}]},
- {attribute,3,on_load,{init,0}},
- {function,5,init,0,
- [{clause,5,[],[],
- [{op,6,'!',
- {atom,6,Master},
- {tuple,6,[{atom,6,Mod},{call,6,{atom,6,self},[]}]}},
- {'receive',7,[{clause,8,[{atom,8,go}],[],[{atom,8,ok}]}]}]}]},
- {function,11,ok,0,[{clause,11,[],[],[{atom,11,true}]}]}],
- Forms1 = erl_parse:new_anno(Forms),
- {ok,Mod,Bin} = compile:forms(Forms1, [report]),
- [io:put_chars(erl_pp:form(F)) || F <- Forms1],
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-export([ok/0]).\n",
+ "-on_load({init,0}).\n",
+ "init() ->\n",
+ " '@Master@' ! {on_load_binary,self()},\n",
+ " receive go -> ok end.\n",
+ "ok() -> true.\n"]),
+ {ok,Mod,Bin} = merl:compile(Tree),
+ merl:print(Tree),
{Pid1,Ref1} = spawn_monitor(fun() ->
code:load_binary(Mod, File, Bin),
@@ -1423,6 +1446,163 @@ do_on_load_error(ReturnValue) ->
{undef,[{on_load_error,main,[],_}|_]} = Exit
end.
+on_load_update(_Config) ->
+ {Mod,Code1} = on_load_update_code(1),
+ {module,Mod} = code:load_binary(Mod, "", Code1),
+ 42 = Mod:a(),
+ 100 = Mod:b(99),
+ 4 = erlang:trace_pattern({Mod,'_','_'}, true),
+
+ {Mod,Code2} = on_load_update_code(2),
+ {error,on_load_failure} = code:load_binary(Mod, "", Code2),
+ 42 = Mod:a(),
+ 100 = Mod:b(99),
+ {'EXIT',{undef,_}} = (catch Mod:never()),
+ 4 = erlang:trace_pattern({Mod,'_','_'}, false),
+
+ {Mod,Code3} = on_load_update_code(3),
+ {module,Mod} = code:load_binary(Mod, "", Code3),
+ 100 = Mod:c(),
+ {'EXIT',{undef,_}} = (catch Mod:a()),
+ {'EXIT',{undef,_}} = (catch Mod:b(10)),
+ {'EXIT',{undef,_}} = (catch Mod:never()),
+
+ ok.
+
+on_load_update_code(Version) ->
+ Mod = ?FUNCTION_NAME,
+ Tree = on_load_update_code_1(Version, Mod),
+ io:format("Version ~p", [Version]),
+ {ok,Mod,Code} = merl:compile(Tree),
+ merl:print(Tree),
+ io:nl(),
+ {Mod,Code}.
+
+on_load_update_code_1(1, Mod) ->
+ ?Q(["-module('@Mod@').\n",
+ "-export([a/0,b/1]).\n"
+ "-on_load(f/0).\n",
+ "f() -> ok.\n",
+ "a() -> 42.\n"
+ "b(I) -> I+1.\n"]);
+on_load_update_code_1(2, Mod) ->
+ ?Q(["-module('@Mod@').\n",
+ "-export([never/0]).\n"
+ "-on_load(f/0).\n",
+ "f() -> 42 = '@Mod@':a(), 1 = '@Mod@':b(0), fail.\n",
+ "never() -> never.\n"]);
+on_load_update_code_1(3, Mod) ->
+ ?Q(["-module('@Mod@').\n",
+ "-export([c/0]).\n"
+ "-on_load(f/0).\n",
+ "f() -> ok.\n",
+ "c() -> 100.\n"]).
+
+on_load_purge(_Config) ->
+ Mod = ?FUNCTION_NAME,
+ register(Mod, self()),
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "loop() -> loop().\n",
+ "f() ->\n",
+ "'@Mod@' ! {self(),spawn(fun loop/0)},\n",
+ "receive Ack -> Ack end.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+ P = spawn(fun() ->
+ exit(code:load_binary(Mod, "", Code))
+ end),
+ monitor(process, P),
+ receive
+ {Pid1,Pid2} ->
+ monitor(process, Pid2),
+ Pid1 ! ack_and_failure,
+ receive
+ {'DOWN',_,process,P,Exit1} ->
+ {error,on_load_failure} = Exit1
+ end,
+ receive
+ {'DOWN',_,process,Pid2,Exit2} ->
+ io:format("~p\n", [Exit2])
+ after 10000 ->
+ ct:fail(no_down_message)
+ end
+ end.
+
+on_load_self_call(_Config) ->
+ Mod = ?FUNCTION_NAME,
+ register(Mod, self()),
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-export([ext/0]).\n",
+ "-on_load(f/0).\n",
+ "f() ->\n",
+ " '@Mod@' ! (catch '@Mod@':ext()),\n",
+ " ok.\n",
+ "ext() -> good_work.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+
+ {'EXIT',{undef,_}} = on_load_do_load(Mod, Code),
+ good_work = on_load_do_load(Mod, Code),
+
+ ok.
+
+on_load_do_load(Mod, Code) ->
+ spawn(fun() ->
+ {module,Mod} = code:load_binary(Mod, "", Code)
+ end),
+ receive
+ Any -> Any
+ end.
+
+on_load_pending(_Config) ->
+ Mod = ?FUNCTION_NAME,
+ Tree1 = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "f() ->\n",
+ " register('@Mod@', self()),\n",
+ " receive _ -> ok end.\n"]),
+ merl:print(Tree1),
+ {ok,Mod,Code1} = merl:compile(Tree1),
+
+ Tree2 = ?Q(["-module('@Mod@').\n",
+ "-export([t/0]).\n",
+ "t() -> ok.\n"]),
+ merl:print(Tree2),
+ {ok,Mod,Code2} = merl:compile(Tree2),
+
+ Self = self(),
+ {_,Ref1} =
+ spawn_monitor(fun() ->
+ Self ! started1,
+ {module,Mod} = code:load_binary(Mod, "", Code1)
+ end),
+ receive started1 -> ok end,
+ timer:sleep(10),
+ {_,Ref2} =
+ spawn_monitor(fun() ->
+ Self ! started2,
+ {module,Mod} = code:load_binary(Mod, "", Code2),
+ ok = Mod:t()
+ end),
+ receive started2 -> ok end,
+ receive
+ Unexpected ->
+ ct:fail({unexpected,Unexpected})
+ after 100 ->
+ ok
+ end,
+ Mod ! go,
+ receive
+ {'DOWN',Ref1,process,_,normal} -> ok
+ end,
+ receive
+ {'DOWN',Ref2,process,_,normal} -> ok
+ end,
+ ok = Mod:t(),
+ ok.
+
+
%% Test that the native code of early loaded modules is loaded.
native_early_modules(Config) when is_list(Config) ->
case erlang:system_info(hipe_architecture) of
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index bf1548591a..eb58e92224 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -634,7 +634,7 @@ monitor_nodes_nodedown_reason(Config) when is_list(Config) ->
stop_node(N4),
true = net_kernel:disconnect(N2),
TickTime = net_kernel:get_net_ticktime(),
- SleepTime = TickTime + (TickTime div 4),
+ SleepTime = TickTime + (TickTime div 2),
spawn(N3, fun () ->
block_emu(SleepTime*1000),
halt()
@@ -911,15 +911,14 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) ->
%% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message
%% bringing up the connection).
- no_msgs(500),
{nodeup, Node} = receive Msg1 -> Msg1 end,
- {NodeMsg, 1} = receive Msg2 -> Msg2 end,
+ {NodeMsg, N} = receive Msg2 -> Msg2 end,
%% msg stream has begun, kill the node
RemotePid ! {self(), kill_it},
%% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}'
%% message.
- {nodedown, Node} = flush_node_msgs(NodeMsg, 2),
+ {nodedown, Node} = flush_node_msgs(NodeMsg, N+1),
no_msgs(500),
Mon = erlang:monitor(process, MN),
@@ -932,8 +931,10 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) ->
flush_node_msgs(NodeMsg, No) ->
case receive Msg -> Msg end of
- {NodeMsg, No} -> flush_node_msgs(NodeMsg, No+1);
- OtherMsg -> OtherMsg
+ {NodeMsg, N} when N >= No ->
+ flush_node_msgs(NodeMsg, N+1);
+ OtherMsg ->
+ OtherMsg
end.
node_loop_send(Pid, Msg, No) ->
diff --git a/lib/kernel/test/global_group_SUITE.erl b/lib/kernel/test/global_group_SUITE.erl
index 06a5b7fcfe..594ee6b537 100644
--- a/lib/kernel/test/global_group_SUITE.erl
+++ b/lib/kernel/test/global_group_SUITE.erl
@@ -1153,6 +1153,16 @@ test_exit(Config) when is_list(Config) ->
rpc:call(Cp1, global_group, send, [king, "The message"]),
undefined = rpc:call(Cp1, global_group, whereis_name, [king]),
+ % make sure the search process really exits after every global_group operations
+ ProcessCount0 = rpc:call(Cp1, erlang, system_info, [process_count]),
+ _ = rpc:call(Cp1, global_group, whereis_name, [{node, Cp1nn}, whatever_pid_name]),
+ ProcessCount1 = rpc:call(Cp1, erlang, system_info, [process_count]),
+ _ = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]),
+ ProcessCount2 = rpc:call(Cp1, erlang, system_info, [process_count]),
+ _ = rpc:call(Cp1, global_group, send, [{node, Cp1nn}, whatever_pid_name, msg]),
+ ProcessCount3 = rpc:call(Cp1, erlang, system_info, [process_count]),
+ ProcessCount0 = ProcessCount1 = ProcessCount2 = ProcessCount3,
+
%% stop the nodes, and make sure names are released.
stop_node(Cp1),
stop_node(Cp2),
diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl
index 000994751f..be23a1933f 100644
--- a/lib/kernel/test/seq_trace_SUITE.erl
+++ b/lib/kernel/test/seq_trace_SUITE.erl
@@ -498,7 +498,13 @@ match_set_seq_token(Config) when is_list(Config) ->
exit(P2, timeout),
{error, "Test node hung"}
end,
- ok = check_match_set_seq_token_log(Lbl, Log),
+
+ %% Sort the log on Pid, as events from different processes
+ %% are not guaranteed to arrive in a certain order to the
+ %% tracer
+ SortedLog = lists:keysort(2, Log),
+
+ ok = check_match_set_seq_token_log(Lbl, SortedLog),
%%
stop_node(Sandbox),
ok.
@@ -551,13 +557,13 @@ do_match_set_seq_token(Label) ->
check_match_set_seq_token_log(
Label,
- [{trace,C,call,{?MODULE,countdown,[B,Ref]}, {0,Label,0,C,0}},
+ [{trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,2,B,1}},
+ {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,4,B,3}},
+ {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,6,B,5}},
+ {trace,C,call,{?MODULE,countdown,[B,Ref]}, {0,Label,0,C,0}},
{trace,C,call,{?MODULE,countdown,[B,Ref,3]},{0,Label,0,C,0}},
- {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,2,B,1}},
{trace,C,call,{?MODULE,countdown,[B,Ref,2]},{0,Label,2,B,1}},
- {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,4,B,3}},
{trace,C,call,{?MODULE,countdown,[B,Ref,1]},{0,Label,4,B,3}},
- {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,6,B,5}},
{trace,C,call,{?MODULE,countdown,[B,Ref,0]},{0,Label,6,B,5}}
]) ->
ok;
diff --git a/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile
index ae24bc72de..08a00e6aba 100644
--- a/lib/mnesia/src/Makefile
+++ b/lib/mnesia/src/Makefile
@@ -43,6 +43,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/mnesia-$(VSN)
# ----------------------------------------------------
MODULES= \
mnesia \
+ mnesia_backend_type \
mnesia_backup \
mnesia_bup \
mnesia_checkpoint \
@@ -50,6 +51,7 @@ MODULES= \
mnesia_controller \
mnesia_dumper\
mnesia_event \
+ mnesia_ext_sup \
mnesia_frag \
mnesia_frag_hash \
mnesia_frag_old_hash \
diff --git a/lib/mnesia/src/mnesia.app.src b/lib/mnesia/src/mnesia.app.src
index c78a7cba1e..006ad4bac1 100644
--- a/lib/mnesia/src/mnesia.app.src
+++ b/lib/mnesia/src/mnesia.app.src
@@ -3,6 +3,7 @@
{vsn, "%VSN%"},
{modules, [
mnesia,
+ mnesia_backend_type,
mnesia_backup,
mnesia_bup,
mnesia_checkpoint,
@@ -10,6 +11,7 @@
mnesia_controller,
mnesia_dumper,
mnesia_event,
+ mnesia_ext_sup,
mnesia_frag,
mnesia_frag_hash,
mnesia_frag_old_hash,
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index e3d9bb1484..9586adbf93 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -82,7 +82,8 @@
system_info/0, % Not for public use
%% Database mgt
- create_schema/1, delete_schema/1,
+ create_schema/1, create_schema/2, delete_schema/1,
+ add_backend_type/2,
backup/1, backup/2, traverse_backup/4, traverse_backup/6,
install_fallback/1, install_fallback/2,
uninstall_fallback/0, uninstall_fallback/1,
@@ -197,6 +198,9 @@ e_has_var(X, Pos) ->
%% Start and stop
start() ->
+ start([]).
+
+start_() ->
{Time , Res} = timer:tc(application, start, [?APPLICATION, temporary]),
Secs = Time div 1000000,
@@ -232,7 +236,7 @@ patched_start([{Env, Val} | Tail]) when is_atom(Env) ->
patched_start([Head | _]) ->
{error, {bad_type, Head}};
patched_start([]) ->
- start().
+ start_().
stop() ->
case application:stop(?APPLICATION) of
@@ -297,6 +301,7 @@ ms() ->
%% Keep these last in the list, so
%% mnesia_sup kills these last
+ mnesia_ext_sup,
mnesia_monitor,
mnesia_event
].
@@ -556,22 +561,16 @@ write(_Tid, _Ts, Tab, Val, LockKind) ->
abort({bad_type, Tab, Val, LockKind}).
write_to_store(Tab, Store, Oid, Val) ->
- case ?catch_val({Tab, record_validation}) of
- {RecName, Arity, Type}
- when tuple_size(Val) == Arity, RecName == element(1, Val) ->
- case Type of
- bag ->
- ?ets_insert(Store, {Oid, Val, write});
- _ ->
- ?ets_delete(Store, Oid),
- ?ets_insert(Store, {Oid, Val, write})
- end,
- ok;
- {'EXIT', _} ->
- abort({no_exists, Tab});
- _ ->
- abort({bad_type, Val})
- end.
+ {_, _, Type} = mnesia_lib:validate_record(Tab, Val),
+ Oid = {Tab, element(2, Val)},
+ case Type of
+ bag ->
+ ?ets_insert(Store, {Oid, Val, write});
+ _ ->
+ ?ets_delete(Store, Oid),
+ ?ets_insert(Store, {Oid, Val, write})
+ end,
+ ok.
delete({Tab, Key}) ->
delete(Tab, Key, write);
@@ -1548,16 +1547,9 @@ dirty_write(Tab, Val) ->
do_dirty_write(SyncMode, Tab, Val)
when is_atom(Tab), Tab /= schema, is_tuple(Val), tuple_size(Val) > 2 ->
- case ?catch_val({Tab, record_validation}) of
- {RecName, Arity, _Type}
- when tuple_size(Val) == Arity, RecName == element(1, Val) ->
- Oid = {Tab, element(2, Val)},
- mnesia_tm:dirty(SyncMode, {Oid, Val, write});
- {'EXIT', _} ->
- abort({no_exists, Tab});
- _ ->
- abort({bad_type, Val})
- end;
+ {_, _, _} = mnesia_lib:validate_record(Tab, Val),
+ Oid = {Tab, element(2, Val)},
+ mnesia_tm:dirty(SyncMode, {Oid, Val, write});
do_dirty_write(_SyncMode, Tab, Val) ->
abort({bad_type, Tab, Val}).
@@ -1609,8 +1601,8 @@ dirty_update_counter(Tab, Key, Incr) ->
do_dirty_update_counter(SyncMode, Tab, Key, Incr)
when is_atom(Tab), Tab /= schema, is_integer(Incr) ->
- case ?catch_val({Tab, record_validation}) of
- {RecName, 3, set} ->
+ case mnesia_lib:validate_key(Tab, Key) of
+ {RecName, 3, Type} when Type == set; Type == ordered_set ->
Oid = {Tab, Key},
mnesia_tm:dirty(SyncMode, {Oid, {RecName, Incr}, update_counter});
_ ->
@@ -1910,6 +1902,8 @@ raw_table_info(Tab, Item) ->
info_reply(?ets_info(Tab, Item), Tab, Item);
disc_only_copies ->
info_reply(dets:info(Tab, Item), Tab, Item);
+ {ext, Alias, Mod} ->
+ info_reply(catch Mod:info(Alias, Tab, Item), Tab, Item);
unknown ->
bad_info_reply(Tab, Item)
end
@@ -2022,15 +2016,26 @@ display_tab_info() ->
MasterTabs = mnesia_recover:get_master_node_tables(),
io:format("master node tables = ~p~n", [lists:sort(MasterTabs)]),
+ case get_backend_types() of
+ [] -> ok;
+ Ts -> list_backend_types(Ts, "backend types = ")
+ end,
+
+ case get_index_plugins() of
+ [] -> ok;
+ Ps -> list_index_plugins(Ps, "index plugins = ")
+ end,
+
Tabs = system_info(tables),
- {Unknown, Ram, Disc, DiscOnly} =
- lists:foldl(fun storage_count/2, {[], [], [], []}, Tabs),
+ {Unknown, Ram, Disc, DiscOnly, Ext} =
+ lists:foldl(fun storage_count/2, {[], [], [], [], []}, Tabs),
io:format("remote = ~p~n", [lists:sort(Unknown)]),
io:format("ram_copies = ~p~n", [lists:sort(Ram)]),
io:format("disc_copies = ~p~n", [lists:sort(Disc)]),
io:format("disc_only_copies = ~p~n", [lists:sort(DiscOnly)]),
+ [io:format("~-19s= ~p~n", [atom_to_list(A), Ts]) || {A,Ts} <- Ext],
Rfoldl = fun(T, Acc) ->
Rpat =
@@ -2038,7 +2043,7 @@ display_tab_info() ->
read_only ->
lists:sort([{A, read_only} || A <- val({T, active_replicas})]);
read_write ->
- table_info(T, where_to_commit)
+ [fix_wtc(W) || W <- table_info(T, where_to_commit)]
end,
case lists:keysearch(Rpat, 1, Acc) of
{value, {_Rpat, Rtabs}} ->
@@ -2051,12 +2056,60 @@ display_tab_info() ->
Rdisp = fun({Rpat, Rtabs}) -> io:format("~p = ~p~n", [Rpat, Rtabs]) end,
lists:foreach(Rdisp, lists:sort(Repl)).
-storage_count(T, {U, R, D, DO}) ->
+get_backend_types() ->
+ case ?catch_val({schema, user_property, mnesia_backend_types}) of
+ {'EXIT', _} ->
+ [];
+ {mnesia_backend_types, Ts} ->
+ lists:sort(Ts)
+ end.
+
+get_index_plugins() ->
+ case ?catch_val({schema, user_property, mnesia_index_plugins}) of
+ {'EXIT', _} ->
+ [];
+ {mnesia_index_plugins, Ps} ->
+ lists:sort(Ps)
+ end.
+
+
+list_backend_types([{A,M} | T] = Ts, Legend) ->
+ Indent = [$\s || _ <- Legend],
+ W = integer_to_list(
+ lists:foldl(fun({Alias,_}, Wa) ->
+ erlang:max(Wa, length(atom_to_list(Alias)))
+ end, 0, Ts)),
+ io:fwrite(Legend ++ "~-" ++ W ++ "s - ~s~n",
+ [atom_to_list(A), atom_to_list(M)]),
+ [io:fwrite(Indent ++ "~-" ++ W ++ "s - ~s~n",
+ [atom_to_list(A1), atom_to_list(M1)])
+ || {A1,M1} <- T].
+
+list_index_plugins([{N,M,F} | T] = Ps, Legend) ->
+ Indent = [$\s || _ <- Legend],
+ W = integer_to_list(
+ lists:foldl(fun({N1,_,_}, Wa) ->
+ erlang:max(Wa, length(pp_ix_name(N1)))
+ end, 0, Ps)),
+ io:fwrite(Legend ++ "~-" ++ W ++ "s - ~s:~s~n",
+ [pp_ix_name(N), atom_to_list(M), atom_to_list(F)]),
+ [io:fwrite(Indent ++ "~-" ++ W ++ "s - ~s:~s~n",
+ [pp_ix_name(N1), atom_to_list(M1), atom_to_list(F1)])
+ || {N1,M1,F1} <- T].
+
+pp_ix_name(N) ->
+ lists:flatten(io_lib:fwrite("~w", [N])).
+
+fix_wtc({N, {ext,A,_}}) -> {N, A};
+fix_wtc({N,A}) when is_atom(A) -> {N, A}.
+
+storage_count(T, {U, R, D, DO, Ext}) ->
case table_info(T, storage_type) of
- unknown -> {[T | U], R, D, DO};
- ram_copies -> {U, [T | R], D, DO};
- disc_copies -> {U, R, [T | D], DO};
- disc_only_copies -> {U, R, D, [T | DO]}
+ unknown -> {[T | U], R, D, DO, Ext};
+ ram_copies -> {U, [T | R], D, DO, Ext};
+ disc_copies -> {U, R, [T | D], DO, Ext};
+ disc_only_copies -> {U, R, D, [T | DO], Ext};
+ {ext, A, _} -> {U, R, D, DO, orddict:append(A, T, Ext)}
end.
system_info(Item) ->
@@ -2071,9 +2124,10 @@ system_info2(all) ->
system_info2(db_nodes) ->
DiscNs = ?catch_val({schema, disc_copies}),
RamNs = ?catch_val({schema, ram_copies}),
+ ExtNs = ?catch_val({schema, external_copies}),
if
- is_list(DiscNs), is_list(RamNs) ->
- DiscNs ++ RamNs;
+ is_list(DiscNs), is_list(RamNs), is_list(ExtNs) ->
+ DiscNs ++ RamNs ++ ExtNs;
true ->
case mnesia_schema:read_nodes() of
{ok, Nodes} -> Nodes;
@@ -2177,6 +2231,7 @@ system_info2(access_module) -> mnesia_monitor:get_env(access_module);
system_info2(auto_repair) -> mnesia_monitor:get_env(auto_repair);
system_info2(is_running) -> mnesia_lib:is_running();
system_info2(backup_module) -> mnesia_monitor:get_env(backup_module);
+system_info2(backend_types) -> mnesia_schema:backend_types();
system_info2(event_module) -> mnesia_monitor:get_env(event_module);
system_info2(debug) -> mnesia_monitor:get_env(debug);
system_info2(dump_log_load_regulation) -> mnesia_monitor:get_env(dump_log_load_regulation);
@@ -2213,6 +2268,7 @@ system_info_items(yes) ->
[
access_module,
auto_repair,
+ backend_types,
backup_module,
checkpoints,
db_nodes,
@@ -2306,11 +2362,17 @@ load_mnesia_or_abort() ->
%% Database mgt
create_schema(Ns) ->
- mnesia_bup:create_schema(Ns).
+ create_schema(Ns, []).
+
+create_schema(Ns, Properties) ->
+ mnesia_bup:create_schema(Ns, Properties).
delete_schema(Ns) ->
mnesia_schema:delete_schema(Ns).
+add_backend_type(Alias, Module) ->
+ mnesia_schema:add_backend_type(Alias, Module).
+
backup(Opaque) ->
mnesia_log:backup(Opaque).
diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl
index c58b4cfccd..0716dd87c8 100644
--- a/lib/mnesia/src/mnesia.hrl
+++ b/lib/mnesia/src/mnesia.hrl
@@ -68,6 +68,7 @@
ram_copies = [], % [Node]
disc_copies = [], % [Node]
disc_only_copies = [], % [Node]
+ external_copies = [], % [{{Alias,Mod},[Node]}]
load_order = 0, % Integer
access_mode = read_write, % read_write | read_only
majority = false, % true | false
@@ -103,7 +104,7 @@
ram_copies = [],
disc_copies = [],
disc_only_copies = [],
- snmp = [],
+ ext = [],
schema_ops = []
}).
diff --git a/lib/mnesia/src/mnesia_backend_type.erl b/lib/mnesia/src/mnesia_backend_type.erl
new file mode 100644
index 0000000000..4791b82a01
--- /dev/null
+++ b/lib/mnesia/src/mnesia_backend_type.erl
@@ -0,0 +1,115 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Behaviour definition for mnesia backend types.
+%%
+
+%%%header_doc_include
+
+-module(mnesia_backend_type).
+
+-export([behaviour_info/1]).
+
+%%%header_doc_include
+
+%%%impl_doc_include
+
+%% Note that mnesia considers all callbacks mandatory!!
+%%
+behaviour_info(callbacks) ->
+ [
+ {add_aliases, 1}, % (Aliases) -> ok
+ {check_definition, 4}, % (TypeAlias, Tab, Nodes, Properties) -> ok
+ {close_table, 2}, % (TypeAlias, Tab) -> ok
+ {create_table, 3}, % (TypeAlias, Tab, Properties) -> ok
+ {delete, 3}, % (TypeAlias, Tab, Key) -> true | ok
+ {delete_table, 2}, % (TypeAlias, Tab) -> ok
+ {first, 2}, % (TypeAlias, Tab) -> Key::Term | '$end_of_table'
+ {fixtable, 3}, % (TypeAlias, Tab, Bool) -> ok | true
+ {last, 2}, % (TypeAlias, Tab) -> Key::Term | '$end_of_table'
+ {index_is_consistent,3}, % (TypeAlias, IxTag, Bool) -> ok
+ {init_backend, 0}, % () -> ok
+ {info, 3}, % (TypeAlias, Tab, Item) -> Term
+ {insert, 3}, % (TypeAlias, Tab, Object) -> ok
+ {lookup, 3}, % (TypeAlias, Tab, Key) -> [Objects]
+ {is_index_consistent,2}, % (TypeAlias, IxTag) -> Bool
+ {load_table, 4}, % (TypeAlias, Tab, Reason, CsList) -> ok
+ {match_delete, 3}, % (TypeAlias, Tab, Pattern) -> ok
+ {next, 3}, % (TypeAlias, Tab, Key) -> Key::Term | '$end_of_table'
+ {prev, 3}, % (TypeAlias, Tab, Key) -> Key::Term | '$end_of_table'
+ {receiver_first_message, 4}, % (Sender, FirstMsg, Alias, Tab) -> {Size, State}
+ {receive_data, 5}, % (Data, Alias, Name, Sender, State) -> {more, State} | {{more, Msg}, State}
+ {receive_done, 4}, % (Alias, Tab, Sender, State) -> ok
+ {real_suffixes, 0}, % () -> [FileSuffix]
+ {remove_aliases, 1}, % (Aliases) -> ok
+ {repair_continuation, 2}, % (Continuation, MatchSpec) -> Continuation
+ {select, 1}, % (Continuation) -> {[Match], Continuation'} | '$end_of_table'
+ {select, 3}, % (TypeAlias, Tab, Pattern) -> {[Match], Continuation'} | '$end_of_table'
+ {select, 4}, % (TypeAlias, Tab, MatchSpec, Limit) {[Match], Continuation'} | '$end_of_table'
+ {sender_init, 4}, % (TypeAlias, Tab, LoadReason, Pid) ->
+ % {standard, Init(), Chunk()} | {Init(), Chunk()}
+ {semantics, 2}, % (TypeAlias, storage | types | index_fun | index_types) ->
+ % ram_copies | disc_copies, set | ordered_set | bag, fun(), ordered | bag
+ {slot, 3}, % (TypeAlias, Tab, Pos) -> '$end_of_table' | Objects | {error, Reason}
+ {sync_close_table, 2}, % (TypeAlias, Tab) -> ok
+ {tmp_suffixes, 0}, % () -> [FileSuffix]
+ {update_counter, 4}, % (TypeAlias, Tab, Counter, Val) -> NewVal
+ {validate_key, 6}, % (TypeAlias, Tab, RecName, Arity, Type, Key) -> {RecName, Arity, Type}
+ {validate_record, 6} % (TypeAlias, Tab, RecName, Arity, Type, Obj) -> {RecName, Arity, Type}
+ ].
+
+%%%impl_doc_include
+
+%% -type tab() :: atom().
+%% -type alias() :: atom().
+%% -type rec_name() :: atom().
+%% -type type() :: set | bag | ordered_set.
+%% -type proplist() :: [{atom(), any()}].
+%% -type key() :: any().
+%% -type db_object() :: tuple().
+
+%% -type matchspec() :: ets:match_spec().
+%% -type limit() :: integer() | infinity.
+
+%% -type cont_fun() :: any().
+%% -type cont() :: '$end_of_table' | cont_fun().
+
+%% -callback check_definition(alias(), tab(), [node()], proplist()) -> ok.
+%% -callback create_table(alias(), tab(), proplist()) -> tab().
+%% -callback load_table(alias(), tab(), any()) -> ok.
+%% -callback delete_table(alias(), tab()) -> ok.
+%% -callback first(alias(), tab()) -> key().
+%% -callback last(alias(), tab()) -> key().
+%% -callback next(alias(), tab(), key()) -> key().
+%% -callback prev(alias(), tab(), key()) -> key().
+%% -callback insert(alias(), tab(), db_object()) -> ok.
+%% -callback lookup(alias(), tab(), key()) -> [db_object()].
+%% -callback delete(alias(), tab(), key()) -> ok.
+%% -callback update_counter(alias(), tab(), key(), integer()) -> integer().
+%% -callback select(cont()) -> {list(), cont()}.
+%% -callback select(alias(), tab(), matchspec()) -> list() | '$end_of_table'.
+%% -callback select(alias(), tab(), matchspec(), limit()) -> {list(), cont()}.
+%% -callback slot(alias(), tab(), integer()) -> key().
+%% -callback validate_key(alias(), tab(), rec_name(), arity(), type(), key()) ->
+%% {rec_name(), arity(), type()}.
+%% -callback validate_record(alias(),tab(),rec_name(),arity(),type(),db_obj()) ->
+%% {rec_name(), arity(), type()}.
+%% -callback repair_continuation(cont(), matchspec()) -> cont().
diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl
index 8b1143a352..3e55deb958 100644
--- a/lib/mnesia/src/mnesia_bup.erl
+++ b/lib/mnesia/src/mnesia_bup.erl
@@ -28,6 +28,7 @@
fallback_exists/0,
tm_fallback_start/1,
create_schema/1,
+ create_schema/2,
install_fallback/1,
install_fallback/2,
uninstall_fallback/0,
@@ -46,7 +47,7 @@
uninstall_fallback_master/2,
local_uninstall_fallback/2,
do_traverse_backup/7,
- trav_apply/4
+ trav_apply/5
]).
-include("mnesia.hrl").
@@ -81,7 +82,8 @@ iterate(Mod, Fun, Opaque, Acc) ->
R = #restore{bup_module = Mod, bup_data = Opaque},
try read_schema_section(R) of
{R2, {Header, Schema, Rest}} ->
- try iter(R2, Header, Schema, Fun, Acc, Rest) of
+ Ext = get_ext_types(Schema),
+ try iter(R2, Header, Schema, Ext, Fun, Acc, Rest) of
{ok, R3, Res} ->
close_read(R3),
{ok, Res}
@@ -96,17 +98,32 @@ iterate(Mod, Fun, Opaque, Acc) ->
Err
end.
-iter(R, Header, Schema, Fun, Acc, []) ->
+get_ext_types(Schema) ->
+ try
+ List = lookup_schema(schema, Schema),
+ case lists:keyfind(user_properties, 1, List) of
+ {_, Props} ->
+ proplists:get_value(
+ mnesia_backend_types, Props, []);
+ false ->
+ []
+ end
+ catch
+ throw:{error, {"Cannot lookup",_}} ->
+ []
+ end.
+
+iter(R, Header, Schema, Ext, Fun, Acc, []) ->
case safe_apply(R, read, [R#restore.bup_data]) of
{R2, []} ->
- Res = Fun([], Header, Schema, Acc),
+ Res = Fun([], Header, Schema, Ext, Acc),
{ok, R2, Res};
{R2, BupItems} ->
- iter(R2, Header, Schema, Fun, Acc, BupItems)
+ iter(R2, Header, Schema, Ext, Fun, Acc, BupItems)
end;
-iter(R, Header, Schema, Fun, Acc, BupItems) ->
- Acc2 = Fun(BupItems, Header, Schema, Acc),
- iter(R, Header, Schema, Fun, Acc2, []).
+iter(R, Header, Schema, Ext, Fun, Acc, BupItems) ->
+ Acc2 = Fun(BupItems, Header, Schema, Ext, Acc),
+ iter(R, Header, Schema, Ext, Fun, Acc2, []).
-spec safe_apply(#restore{}, atom(), list()) -> tuple().
safe_apply(R, write, [_, Items]) when Items =:= [] ->
@@ -262,7 +279,7 @@ convert_0_1(Schema) ->
Cs = mnesia_schema:list2cs(List),
convert_0_1(Schema2, [], Cs);
false ->
- List = mnesia_schema:get_initial_schema(disc_copies, [node()]),
+ List = mnesia_schema:get_initial_schema(disc_copies, [node()], []),
Cs = mnesia_schema:list2cs(List),
convert_0_1(Schema, [], Cs)
end.
@@ -310,16 +327,19 @@ schema2bup({schema, Tab, TableDef}) ->
%% Create schema on the given nodes
%% Requires that old schemas has been deleted
%% Returns ok | {error, Reason}
-create_schema([]) ->
- create_schema([node()]);
-create_schema(Ns) when is_list(Ns) ->
+create_schema(Nodes) ->
+ create_schema(Nodes, []).
+
+create_schema([], Props) ->
+ create_schema([node()], Props);
+create_schema(Ns, Props) when is_list(Ns), is_list(Props) ->
case is_set(Ns) of
true ->
- create_schema(Ns, mnesia_schema:ensure_no_schema(Ns));
+ create_schema(Ns, mnesia_schema:ensure_no_schema(Ns), Props);
false ->
{error, {combine_error, Ns}}
end;
-create_schema(Ns) ->
+create_schema(Ns, _Props) ->
{error, {badarg, Ns}}.
is_set(List) when is_list(List) ->
@@ -327,7 +347,7 @@ is_set(List) when is_list(List) ->
is_set(_) ->
false.
-create_schema(Ns, ok) ->
+create_schema(Ns, ok, Props) ->
%% Ensure that we access the intended Mnesia
%% directory. This function may not be called
%% during startup since it will cause the
@@ -346,7 +366,7 @@ create_schema(Ns, ok) ->
Str = mk_str(),
File = mnesia_lib:dir(Str),
file:delete(File),
- try make_initial_backup(Ns, File, Mod) of
+ try make_initial_backup(Ns, File, Mod, Props) of
{ok, _Res} ->
case do_install_fallback(File, Mod) of
ok ->
@@ -363,9 +383,9 @@ create_schema(Ns, ok) ->
{error, Reason} ->
{error, Reason}
end;
-create_schema(_Ns, {error, Reason}) ->
+create_schema(_Ns, {error, Reason}, _) ->
{error, Reason};
-create_schema(_Ns, Reason) ->
+create_schema(_Ns, Reason, _) ->
{error, Reason}.
mk_str() ->
@@ -373,7 +393,10 @@ mk_str() ->
lists:concat([node()] ++ Now ++ ".TMP").
make_initial_backup(Ns, Opaque, Mod) ->
- Orig = mnesia_schema:get_initial_schema(disc_copies, Ns),
+ make_initial_backup(Ns, Opaque, Mod, []).
+
+make_initial_backup(Ns, Opaque, Mod, Props) ->
+ Orig = mnesia_schema:get_initial_schema(disc_copies, Ns, Props),
Modded = proplists:delete(storage_properties, proplists:delete(majority, Orig)),
Schema = [{schema, schema, Modded}],
O2 = do_apply(Mod, open_write, [Opaque], Opaque),
@@ -486,15 +509,15 @@ install_fallback_master(ClientPid, FA) ->
State = {start, FA},
Opaque = FA#fallback_args.opaque,
Mod = FA#fallback_args.module,
- Res = iterate(Mod, fun restore_recs/4, Opaque, State),
+ Res = iterate(Mod, fun restore_recs/5, Opaque, State),
unlink(ClientPid),
ClientPid ! {self(), Res},
exit(shutdown).
-restore_recs(_, _, _, stop) ->
+restore_recs(_, _, _, _, stop) ->
throw({error, "restore_recs already stopped"});
-restore_recs(Recs, Header, Schema, {start, FA}) ->
+restore_recs(Recs, Header, Schema, Ext, {start, FA}) ->
%% No records in backup
Schema2 = convert_schema(Header#log_header.log_version, Schema),
CreateList = lookup_schema(schema, Schema2),
@@ -505,19 +528,19 @@ restore_recs(Recs, Header, Schema, {start, FA}) ->
Args = [self(), FA],
Pids = [spawn_link(N, ?MODULE, fallback_receiver, Args) || N <- Ns],
send_fallback(Pids, {start, Header, Schema2}),
- Res = restore_recs(Recs, Header, Schema2, Pids),
+ Res = restore_recs(Recs, Header, Schema2, Ext, Pids),
global:del_lock({{mnesia_table_lock, schema}, self()}, Ns),
Res
catch _:Reason ->
throw({error, {"Bad schema in restore_recs", Reason}})
end;
-restore_recs([], _Header, _Schema, Pids) ->
+restore_recs([], _Header, _Schema, _Ext, Pids) ->
send_fallback(Pids, swap),
send_fallback(Pids, stop),
stop;
-restore_recs(Recs, _, _, Pids) ->
+restore_recs(Recs, _, _, _, Pids) ->
send_fallback(Pids, {records, Recs}),
Pids.
@@ -716,7 +739,7 @@ do_fallback_start(true, false) ->
BupFile = fallback_bup(),
Mod = mnesia_backup,
LocalTabs = ?ets_new_table(mnesia_local_tables, [set, public, {keypos, 2}]),
- case iterate(Mod, fun restore_tables/4, BupFile, {start, LocalTabs}) of
+ case iterate(Mod, fun restore_tables/5, BupFile, {start, LocalTabs}) of
{ok, _Res} ->
?SAFE(dets:close(schema)),
TmpSchema = mnesia_lib:tab2tmp(schema),
@@ -737,23 +760,24 @@ do_fallback_start(true, false) ->
{error, {"Cannot start from fallback", Reason}}
end.
-restore_tables(All=[Rec | Recs], Header, Schema, State={local, LocalTabs, LT}) ->
+restore_tables(All=[Rec | Recs], Header, Schema, Ext,
+ State={local, LocalTabs, LT}) ->
Tab = element(1, Rec),
if
Tab =:= LT#local_tab.name ->
Key = element(2, Rec),
(LT#local_tab.add)(Tab, Key, Rec, LT),
- restore_tables(Recs, Header, Schema, State);
+ restore_tables(Recs, Header, Schema, Ext, State);
true ->
NewState = {new, LocalTabs},
- restore_tables(All, Header, Schema, NewState)
+ restore_tables(All, Header, Schema, Ext, NewState)
end;
-restore_tables(All=[Rec | Recs], Header, Schema, {new, LocalTabs}) ->
+restore_tables(All=[Rec | Recs], Header, Schema, Ext, {new, LocalTabs}) ->
Tab = element(1, Rec),
case ?ets_lookup(LocalTabs, Tab) of
[] ->
State = {not_local, LocalTabs, Tab},
- restore_tables(Recs, Header, Schema, State);
+ restore_tables(Recs, Header, Schema, Ext, State);
[LT] when is_record(LT, local_tab) ->
State = {local, LocalTabs, LT},
case LT#local_tab.opened of
@@ -762,38 +786,39 @@ restore_tables(All=[Rec | Recs], Header, Schema, {new, LocalTabs}) ->
(LT#local_tab.open)(Tab, LT),
?ets_insert(LocalTabs,LT#local_tab{opened=true})
end,
- restore_tables(All, Header, Schema, State)
+ restore_tables(All, Header, Schema, Ext, State)
end;
-restore_tables(All=[Rec | Recs], Header, Schema, S = {not_local, LocalTabs, PrevTab}) ->
+restore_tables(All=[Rec | Recs], Header, Schema, Ext,
+ S = {not_local, LocalTabs, PrevTab}) ->
Tab = element(1, Rec),
if
Tab =:= PrevTab ->
- restore_tables(Recs, Header, Schema, S);
+ restore_tables(Recs, Header, Schema, Ext, S);
true ->
State = {new, LocalTabs},
- restore_tables(All, Header, Schema, State)
+ restore_tables(All, Header, Schema, Ext, State)
end;
-restore_tables(Recs, Header, Schema, {start, LocalTabs}) ->
+restore_tables(Recs, Header, Schema, Ext, {start, LocalTabs}) ->
Dir = mnesia_lib:dir(),
OldDir = filename:join([Dir, "OLD_DIR"]),
mnesia_schema:purge_dir(OldDir, []),
mnesia_schema:purge_dir(Dir, [fallback_name()]),
- init_dat_files(Schema, LocalTabs),
+ init_dat_files(Schema, Ext, LocalTabs),
State = {new, LocalTabs},
- restore_tables(Recs, Header, Schema, State);
-restore_tables([], _Header, _Schema, State) ->
+ restore_tables(Recs, Header, Schema, Ext, State);
+restore_tables([], _Header, _Schema, _Ext, State) ->
State.
%% Creates all neccessary dat files and inserts
%% the table definitions in the schema table
%%
%% Returns a list of local_tab tuples for all local tables
-init_dat_files(Schema, LocalTabs) ->
+init_dat_files(Schema, Ext, LocalTabs) ->
TmpFile = mnesia_lib:tab2tmp(schema),
Args = [{file, TmpFile}, {keypos, 2}, {type, set}],
case dets:open_file(schema, Args) of % Assume schema lock
{ok, _} ->
- create_dat_files(Schema, LocalTabs),
+ create_dat_files(Schema, Ext, LocalTabs),
ok = dets:close(schema),
LocalTab = #local_tab{name = schema,
storage_type = disc_copies,
@@ -808,10 +833,10 @@ init_dat_files(Schema, LocalTabs) ->
throw({error, {"Cannot open file", schema, Args, Reason}})
end.
-create_dat_files([{schema, schema, TabDef} | Tail], LocalTabs) ->
+create_dat_files([{schema, schema, TabDef} | Tail], Ext, LocalTabs) ->
ok = dets:insert(schema, {schema, schema, TabDef}),
- create_dat_files(Tail, LocalTabs);
-create_dat_files([{schema, Tab, TabDef} | Tail], LocalTabs) ->
+ create_dat_files(Tail, Ext, LocalTabs);
+create_dat_files([{schema, Tab, TabDef} | Tail], Ext, LocalTabs) ->
TmpFile = mnesia_lib:tab2tmp(Tab),
DatFile = mnesia_lib:tab2dat(Tab),
DclFile = mnesia_lib:tab2dcl(Tab),
@@ -824,56 +849,21 @@ create_dat_files([{schema, Tab, TabDef} | Tail], LocalTabs) ->
mnesia_lib:dets_sync_close(Tab),
file:delete(TmpFile),
- Cs = mnesia_schema:list2cs(TabDef),
+ Cs = mnesia_schema:list2cs(TabDef, Ext),
ok = dets:insert(schema, {schema, Tab, TabDef}),
RecName = Cs#cstruct.record_name,
Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ delete_ext(Storage, Tab),
+ Semantics = mnesia_lib:semantics(Storage, storage),
if
- Storage =:= unknown ->
+ Semantics =:= undefined ->
ok = dets:delete(schema, {schema, Tab}),
- create_dat_files(Tail, LocalTabs);
- Storage =:= disc_only_copies ->
- Args = [{file, TmpFile}, {keypos, 2},
- {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)}],
- Open = fun(T, LT) when T =:= LT#local_tab.name ->
- case mnesia_lib:dets_sync_open(T, Args) of
- {ok, _} ->
- ok;
- {error, Reason} ->
- throw({error, {"Cannot open file", T, Args, Reason}})
- end
- end,
- Add = fun(T, Key, Rec, LT) when T =:= LT#local_tab.name ->
- case Rec of
- {_T, Key} ->
- ok = dets:delete(T, Key);
- (Rec) when T =:= RecName ->
- ok = dets:insert(Tab, Rec);
- (Rec) ->
- Rec2 = setelement(1, Rec, RecName),
- ok = dets:insert(T, Rec2)
- end
- end,
- Close = fun(T, LT) when T =:= LT#local_tab.name ->
- mnesia_lib:dets_sync_close(T)
- end,
- Swap = fun(T, LT) when T =:= LT#local_tab.name ->
- Expunge(),
- case LT#local_tab.opened of
- true ->
- Close(T,LT);
- false ->
- Open(T,LT),
- Close(T,LT)
- end,
- case file:rename(TmpFile, DatFile) of
- ok ->
- ok;
- {error, Reason} ->
- mnesia_lib:fatal("Cannot rename file ~p -> ~p: ~p~n",
- [TmpFile, DatFile, Reason])
- end
- end,
+ create_dat_files(Tail, Ext, LocalTabs);
+ Semantics =:= disc_only_copies ->
+ Open = disc_only_open_fun(Storage, Cs),
+ Add = disc_only_add_fun(Storage, Cs),
+ Close = disc_only_close_fun(Storage),
+ Swap = disc_only_swap_fun(Storage, Expunge, Open, Close),
LocalTab = #local_tab{name = Tab,
storage_type = Storage,
open = Open,
@@ -883,8 +873,8 @@ create_dat_files([{schema, Tab, TabDef} | Tail], LocalTabs) ->
record_name = RecName,
opened = false},
?ets_insert(LocalTabs, LocalTab),
- create_dat_files(Tail, LocalTabs);
- Storage =:= ram_copies; Storage =:= disc_copies ->
+ create_dat_files(Tail, Ext, LocalTabs);
+ Semantics =:= ram_copies; Storage =:= disc_copies ->
Open = fun(T, LT) when T =:= LT#local_tab.name ->
mnesia_log:open_log({?MODULE, T},
mnesia_log:dcl_log_header(),
@@ -945,18 +935,97 @@ create_dat_files([{schema, Tab, TabDef} | Tail], LocalTabs) ->
opened = false
},
?ets_insert(LocalTabs, LocalTab),
- create_dat_files(Tail, LocalTabs)
+ create_dat_files(Tail, Ext, LocalTabs);
+ true ->
+ error({unknown_semantics, [{semantics, Semantics},
+ {tabdef, TabDef},
+ {ext, Ext}]})
end;
-create_dat_files([{schema, Tab} | Tail], LocalTabs) ->
+create_dat_files([{schema, Tab} | Tail], Ext, LocalTabs) ->
?ets_delete(LocalTabs, Tab),
ok = dets:delete(schema, {schema, Tab}),
TmpFile = mnesia_lib:tab2tmp(Tab),
mnesia_lib:dets_sync_close(Tab),
file:delete(TmpFile),
- create_dat_files(Tail, LocalTabs);
-create_dat_files([], _LocalTabs) ->
+ create_dat_files(Tail, Ext, LocalTabs);
+create_dat_files([], _Ext, _LocalTabs) ->
ok.
+delete_ext({ext, Alias, Mod}, Tab) ->
+ Mod:close_table(Alias, Tab),
+ Mod:delete_table(Alias, Tab),
+ ok;
+delete_ext(_, _) ->
+ ok.
+
+
+disc_only_open_fun(disc_only_copies, #cstruct{name = Tab} =Cs) ->
+ TmpFile = mnesia_lib:tab2tmp(Tab),
+ Args = [{file, TmpFile}, {keypos, 2},
+ {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)}],
+ fun(T, LT) when T =:= LT#local_tab.name ->
+ case mnesia_lib:dets_sync_open(T, Args) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ throw({error, {"Cannot open file", T, Args, Reason}})
+ end
+ end;
+disc_only_open_fun({ext,Alias,Mod}, Cs) ->
+ fun(T, LT) when T =:= LT#local_tab.name ->
+ ok = Mod:load_table(Alias, T, restore, mnesia_schema:cs2list(Cs))
+ end.
+
+disc_only_add_fun(Storage, #cstruct{name = Tab,
+ record_name = RecName}) ->
+ fun(T, Key, Rec, #local_tab{name = T}) when T =:= Tab->
+ case Rec of
+ {_T, Key} ->
+ ok = mnesia_lib:db_erase(Storage, T, Key);
+ (Rec) when T =:= RecName ->
+ ok = mnesia_lib:db_put(Storage, T, Rec);
+ (Rec) ->
+ ok = mnesia_lib:db_put(Storage, T,
+ setelement(1, Rec, RecName))
+ end
+ end.
+
+disc_only_close_fun(disc_only_copies) ->
+ fun(T, LT) when T =:= LT#local_tab.name ->
+ mnesia_lib:dets_sync_close(T)
+ end;
+disc_only_close_fun({ext, Alias, Mod}) ->
+ fun(T, _LT) ->
+ Mod:sync_close_table(Alias, T)
+ end.
+
+
+disc_only_swap_fun(disc_only_copies, Expunge, Open, Close) ->
+ fun(T, LT) when T =:= LT#local_tab.name ->
+ TmpFile = mnesia_lib:tab2tmp(T),
+ DatFile = mnesia_lib:tab2dat(T),
+ Expunge(),
+ case LT#local_tab.opened of
+ true ->
+ Close(T,LT);
+ false ->
+ Open(T,LT),
+ Close(T,LT)
+ end,
+ case file:rename(TmpFile, DatFile) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ mnesia_lib:fatal("Cannot rename file ~p -> ~p: ~p~n",
+ [TmpFile, DatFile, Reason])
+ end
+ end;
+disc_only_swap_fun({ext, _Alias, _Mod}, _Expunge, _Open, Close) ->
+ fun(T, #local_tab{name = T} = LT) ->
+ Close(T, LT)
+ end.
+
+
uninstall_fallback() ->
uninstall_fallback([{scope, global}]).
@@ -1133,7 +1202,7 @@ do_traverse_backup(ClientPid, Source, SourceMod, Target, TargetMod, Fun, Acc) ->
end,
A = {start, Fun, Acc, TargetMod, Iter},
Res =
- case iterate(SourceMod, fun trav_apply/4, Source, A) of
+ case iterate(SourceMod, fun trav_apply/5, Source, A) of
{ok, {iter, _, Acc2, _, Iter2}} when TargetMod =/= read_only ->
try
do_apply(TargetMod, commit_write, [Iter2], Iter2),
@@ -1152,7 +1221,7 @@ do_traverse_backup(ClientPid, Source, SourceMod, Target, TargetMod, Fun, Acc) ->
unlink(ClientPid),
ClientPid ! {iter_done, self(), Res}.
-trav_apply(Recs, _Header, _Schema, {iter, Fun, Acc, Mod, Iter}) ->
+trav_apply(Recs, _Header, _Schema, _Ext, {iter, Fun, Acc, Mod, Iter}) ->
{NewRecs, Acc2} = filter_foldl(Fun, Acc, Recs),
if
Mod =/= read_only, NewRecs =/= [] ->
@@ -1161,7 +1230,7 @@ trav_apply(Recs, _Header, _Schema, {iter, Fun, Acc, Mod, Iter}) ->
true ->
{iter, Fun, Acc2, Mod, Iter}
end;
-trav_apply(Recs, Header, Schema, {start, Fun, Acc, Mod, Iter}) ->
+trav_apply(Recs, Header, Schema, Ext, {start, Fun, Acc, Mod, Iter}) ->
Iter2 =
if
Mod =/= read_only ->
@@ -1169,8 +1238,9 @@ trav_apply(Recs, Header, Schema, {start, Fun, Acc, Mod, Iter}) ->
true ->
Iter
end,
- TravAcc = trav_apply(Schema, Header, Schema, {iter, Fun, Acc, Mod, Iter2}),
- trav_apply(Recs, Header, Schema, TravAcc).
+ TravAcc = trav_apply(Schema, Header, Schema, Ext,
+ {iter, Fun, Acc, Mod, Iter2}),
+ trav_apply(Recs, Header, Schema, Ext, TravAcc).
filter_foldl(Fun, Acc, [Head|Tail]) ->
case Fun(Head, Acc) of
diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl
index 33b28a2f29..9eb939e8d3 100644
--- a/lib/mnesia/src/mnesia_checkpoint.erl
+++ b/lib/mnesia/src/mnesia_checkpoint.erl
@@ -675,6 +675,16 @@ tab2retainer({Tab, Name}) ->
FlatName = lists:flatten(io_lib:write(Name)),
mnesia_lib:dir(lists:concat([?MODULE, "_", Tab, "_", FlatName, ".RET"])).
+retainer_create(_Cp, R, Tab, Name, Ext = {ext, Alias, Mod}) ->
+ T = {Tab, retainer, Name},
+ P = mnesia_schema:cs2list(val({Tab, cstruct})),
+ Mod:delete_table(Alias, T),
+ ok = Mod:create_table(Alias, T, P),
+ Cs = val({Tab, cstruct}),
+ Mod:load_table(Alias, T, {retainer, create_table},
+ mnesia_schema:cs2list(Cs)),
+ dbg_out("Checkpoint retainer created ~p ~p~n", [Name, Tab]),
+ R#retainer{store = {Ext, T}, really_retain = true};
retainer_create(_Cp, R, Tab, Name, disc_only_copies) ->
Fname = tab2retainer({Tab, Name}),
file:delete(Fname),
@@ -734,15 +744,23 @@ traverse_dcd({Cont, Recs}, Log, Fun) -> %% trashed data??
traverse_dcd(eof, _Log, _Fun) ->
ok.
+retainer_get({{ext, Alias, Mod}, Store}, Key) ->
+ Mod:lookup(Alias, Store, Key);
retainer_get({ets, Store}, Key) -> ?ets_lookup(Store, Key);
retainer_get({dets, Store}, Key) -> dets:lookup(Store, Key).
+retainer_put({{ext, Alias, Mod}, Store}, Val) ->
+ Mod:insert(Alias, Store, Val);
retainer_put({ets, Store}, Val) -> ?ets_insert(Store, Val);
retainer_put({dets, Store}, Val) -> dets:insert(Store, Val).
+retainer_first({{ext, Alias, Mod}, Store}) ->
+ Mod:first(Alias, Store);
retainer_first({ets, Store}) -> ?ets_first(Store);
retainer_first({dets, Store}) -> dets:first(Store).
+retainer_next({{ext, Alias, Mod}, Store}, Key) ->
+ Mod:next(Alias, Store, Key);
retainer_next({ets, Store}, Key) -> ?ets_next(Store, Key);
retainer_next({dets, Store}, Key) -> dets:next(Store, Key).
@@ -761,11 +779,16 @@ retainer_next({dets, Store}, Key) -> dets:next(Store, Key).
retainer_fixtable(Tab, Bool) when is_atom(Tab) ->
mnesia_lib:db_fixtable(val({Tab, storage_type}), Tab, Bool);
+retainer_fixtable({Ext = {ext, _, _}, Tab}, Bool) ->
+ mnesia_lib:db_fixtable(Ext, Tab, Bool);
retainer_fixtable({ets, Tab}, Bool) ->
mnesia_lib:db_fixtable(ram_copies, Tab, Bool);
retainer_fixtable({dets, Tab}, Bool) ->
mnesia_lib:db_fixtable(disc_only_copies, Tab, Bool).
+retainer_delete({{ext, Alias, Mod}, Store}) ->
+ Mod:close_table(Alias, Store),
+ Mod:delete_table(Alias, Store);
retainer_delete({ets, Store}) ->
?ets_delete_table(Store);
retainer_delete({dets, Store}) ->
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 58cc66e18b..4791e2e290 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -385,6 +385,8 @@ force_load_table(Tab) when is_atom(Tab), Tab /= schema ->
do_force_load_table(Tab);
disc_only_copies ->
do_force_load_table(Tab);
+ {ext, _, _} ->
+ do_force_load_table(Tab);
unknown ->
set({Tab, load_by_force}, true),
cast({force_load_updated, Tab}),
@@ -1533,8 +1535,8 @@ update_whereabouts(Tab, Node, State) ->
Storage == unknown ->
%% No own copy, continue to read remotely
add_active_replica(Tab, Node),
- NodeST = mnesia_lib:storage_type_at_node(Node, Tab),
- ReadST = mnesia_lib:storage_type_at_node(Read, Tab),
+ NodeST = mnesia_lib:semantics(mnesia_lib:storage_type_at_node(Node, Tab), storage),
+ ReadST = mnesia_lib:semantics(mnesia_lib:storage_type_at_node(Read, Tab), storage),
if %% Avoid reading from disc_only_copies
NodeST == disc_only_copies ->
ignore;
@@ -1588,6 +1590,7 @@ last_consistent_replica(Tab, Downs) ->
Ram = Cs#cstruct.ram_copies,
Disc = Cs#cstruct.disc_copies,
DiscOnly = Cs#cstruct.disc_only_copies,
+ Ext = Cs#cstruct.external_copies,
BetterCopies0 = mnesia_lib:remote_copy_holders(Cs) -- Downs,
BetterCopies = BetterCopies0 -- Ram,
AccessMode = Cs#cstruct.access_mode,
@@ -1620,7 +1623,7 @@ last_consistent_replica(Tab, Downs) ->
false;
Storage == ram_copies ->
if
- Disc == [], DiscOnly == [] ->
+ Disc == [], DiscOnly == [], Ext == [] ->
%% Nobody has copy on disc
{true, {Tab, ram_only}};
true ->
@@ -1865,6 +1868,11 @@ info([Tab | Tail]) ->
dets:info(Tab, size),
dets:info(Tab, file_size),
"bytes on disc");
+ {ext, Alias, Mod} ->
+ info_format(Tab,
+ Mod:info(Alias, Tab, size),
+ Mod:info(Alias, Tab, memory),
+ "words of mem");
_ ->
info_format(Tab,
?ets_info(Tab, size),
diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl
index 7418ab7436..eb02a585a6 100644
--- a/lib/mnesia/src/mnesia_dumper.erl
+++ b/lib/mnesia/src/mnesia_dumper.erl
@@ -38,6 +38,8 @@
needs_dump_ets/1,
raw_dump_table/2,
raw_named_dump_table/2,
+ dump_to_logfile/2,
+ load_from_logfile/3,
start_regulator/0,
opt_dump_log/1,
update/3,
@@ -227,11 +229,11 @@ insert_rec(Rec, InPlace, InitBy, LogV) when is_record(Rec, commit) ->
D = Rec#commit.decision,
case mnesia_recover:wait_for_decision(D, InitBy) of
{Tid, committed} ->
- do_insert_rec(Tid, Rec, InPlace, InitBy, LogV);
+ do_insert_rec(Tid, mnesia_tm:new_cr_format(Rec), InPlace, InitBy, LogV);
{Tid, aborted} ->
case InitBy of
startup ->
- mnesia_schema:undo_prepare_commit(Tid, Rec);
+ mnesia_schema:undo_prepare_commit(Tid, mnesia_tm:new_cr_format(Rec));
_ ->
ok
end
@@ -271,15 +273,30 @@ do_insert_rec(Tid, Rec, InPlace, InitBy, LogV) ->
end
end,
D = Rec#commit.disc_copies,
+ ExtOps = commit_ext(Rec),
insert_ops(Tid, disc_copies, D, InPlace, InitBy, LogV),
+ [insert_ops(Tid, Ext, Ops, InPlace, InitBy, LogV) ||
+ {Ext, Ops} <- ExtOps,
+ storage_semantics(Ext) == disc_copies],
case InitBy of
startup ->
DO = Rec#commit.disc_only_copies,
- insert_ops(Tid, disc_only_copies, DO, InPlace, InitBy, LogV);
+ insert_ops(Tid, disc_only_copies, DO, InPlace, InitBy, LogV),
+ [insert_ops(Tid, Ext, Ops, InPlace, InitBy, LogV) ||
+ {Ext, Ops} <- ExtOps, storage_semantics(Ext) == disc_only_copies];
_ ->
ignore
end.
+commit_ext(#commit{ext = []}) -> [];
+commit_ext(#commit{ext = Ext}) ->
+ case lists:keyfind(ext_copies, 1, Ext) of
+ {_, C} ->
+ lists:foldl(fun({Ext0, Op}, D) ->
+ orddict:append(Ext0, Op, D)
+ end, orddict:new(), C);
+ false -> []
+ end.
update(_Tid, [], _DumperMode) ->
dumped;
@@ -330,14 +347,15 @@ insert_ops(Tid, Storage, [Op | Ops], InPlace, InitBy, Ver) when Ver < "4.3" ->
%% Normal ops
disc_insert(_Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy) ->
- case open_files(Tab, Storage, InPlace, InitBy) of
+ Semantics = storage_semantics(Storage),
+ case open_files(Tab, Semantics, Storage, InPlace, InitBy) of
true ->
- case Storage of
+ case Semantics of
disc_copies when Tab /= schema ->
mnesia_log:append({?MODULE,Tab}, {{Tab, Key}, Val, Op}),
ok;
_ ->
- dets_insert(Op,Tab,Key,Val)
+ dets_insert(Op,Tab,Key,Val,Storage)
end;
false ->
ignore
@@ -349,34 +367,37 @@ disc_insert(_Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy) ->
%% Otherwise we will get a double increment.
%% This is perfect but update_counter is a dirty op.
-dets_insert(Op,Tab,Key,Val) ->
+dets_insert(Op,Tab,Key,Val, Storage0) ->
+ Storage = if Tab == schema -> disc_only_copies;
+ true -> Storage0
+ end,
case Op of
write ->
dets_updated(Tab,Key),
- ok = dets:insert(Tab, Val);
+ ok = mnesia_lib:db_put(Storage, Tab, Val);
delete ->
dets_updated(Tab,Key),
- ok = dets:delete(Tab, Key);
+ ok = mnesia_lib:db_erase(Storage, Tab, Key);
update_counter ->
case dets_incr_counter(Tab,Key) of
true ->
{RecName, Incr} = Val,
- try _ = dets:update_counter(Tab, Key, Incr)
+ try _ = mnesia_lib:db_update_counter(Storage, Tab, Key, Incr)
catch error:_ when Incr < 0 ->
Zero = {RecName, Key, 0},
- ok = dets:insert(Tab, Zero);
+ ok = mnesia_lib:db_put(Storage, Tab, Zero);
error:_ ->
Init = {RecName, Key, Incr},
- ok = dets:insert(Tab, Init)
+ ok = mnesia_lib:db_put(Storage, Tab, Init)
end;
false -> ok
end;
delete_object ->
dets_updated(Tab,Key),
- ok = dets:delete_object(Tab, Val);
+ mnesia_lib:db_match_erase(Storage, Tab, Val);
clear_table ->
dets_cleared(Tab),
- ok = dets:delete_all_objects(Tab)
+ ok = mnesia_lib:db_match_erase(Storage, Tab, '_')
end.
dets_updated(Tab,Key) ->
@@ -431,23 +452,29 @@ insert(_Tid, _Storage, _Tab, _Key, [], _Op, _InPlace, _InitBy) ->
ok;
insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy) ->
+ Semantics = storage_semantics(Storage),
Item = {{Tab, Key}, Val, Op},
case InitBy of
startup ->
disc_insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy);
- _ when Storage == ram_copies ->
+ _ when Semantics == ram_copies ->
mnesia_tm:do_update_op(Tid, Storage, Item),
Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
mnesia_tm:do_snmp(Tid, Snmp);
- _ when Storage == disc_copies ->
+ _ when Semantics == disc_copies ->
disc_insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy),
mnesia_tm:do_update_op(Tid, Storage, Item),
Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
mnesia_tm:do_snmp(Tid, Snmp);
- _ when Storage == disc_only_copies ->
+ _ when Semantics == disc_only_copies ->
+ mnesia_tm:do_update_op(Tid, Storage, Item),
+ Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
+ mnesia_tm:do_snmp(Tid, Snmp);
+
+ _ when element(1, Storage) == ext ->
mnesia_tm:do_update_op(Tid, Storage, Item),
Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
mnesia_tm:do_snmp(Tid, Snmp);
@@ -456,6 +483,9 @@ insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy) ->
ignore
end.
+disc_delete_table(Tab, {ext, Alias, Mod}) ->
+ Mod:close_table(Alias, Tab),
+ Mod:delete_table(Alias, Tab);
disc_delete_table(Tab, Storage) ->
case mnesia_monitor:use_dir() of
true ->
@@ -485,11 +515,14 @@ disc_delete_table(Tab, Storage) ->
ok
end.
-disc_delete_indecies(_Tab, _Cs, Storage) when Storage /= disc_only_copies ->
- ok;
-disc_delete_indecies(Tab, Cs, disc_only_copies) ->
- Indecies = Cs#cstruct.index,
- mnesia_index:del_transient(Tab, Indecies, disc_only_copies).
+disc_delete_indecies(Tab, Cs, Storage) ->
+ case storage_semantics(Storage) of
+ disc_only_copies ->
+ Indecies = Cs#cstruct.index,
+ mnesia_index:del_transient(Tab, Indecies, Storage);
+ _ ->
+ ok
+ end.
insert_op(Tid, Storage, {{Tab, Key}, Val, Op}, InPlace, InitBy) ->
%% Propagate to disc only
@@ -515,6 +548,8 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace,
Cs = mnesia_schema:list2cs(TabDef),
Val = mnesia_schema:insert_cstruct(Tid, Cs, true), % Update ram only
{schema, Tab, _} = Val,
+ FromSem = storage_semantics(FromS),
+ ToSem = storage_semantics(ToS),
case lists:member(N, val({current, db_nodes})) of
true when InitBy /= startup ->
mnesia_controller:add_active_replica(Tab, N, Cs);
@@ -527,62 +562,118 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace,
Dat = mnesia_lib:tab2dat(Tab),
Dcd = mnesia_lib:tab2dcd(Tab),
Dcl = mnesia_lib:tab2dcl(Tab),
+ Logtmp = mnesia_lib:tab2logtmp(Tab),
case {FromS, ToS} of
- {ram_copies, disc_copies} when Tab == schema ->
- ok = ensure_rename(Dmp, Dat);
- {ram_copies, disc_copies} ->
- file:delete(Dcl),
- ok = ensure_rename(Dmp, Dcd);
- {disc_copies, ram_copies} when Tab == schema ->
- mnesia_lib:set(use_dir, false),
- mnesia_monitor:unsafe_close_dets(Tab),
- ok = file:delete(Dat);
- {disc_copies, ram_copies} ->
- _ = 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),
- %% ram_delete_table must be done before init_indecies,
- %% it uses info which is reset in init_indecies,
- %% it doesn't matter, because init_indecies don't use
- %% the ram replica of the table when creating the disc
- %% index; Could be improved :)
- mnesia_schema:ram_delete_table(Tab, FromS),
- PosList = Cs#cstruct.index,
- mnesia_index:init_indecies(Tab, disc_only_copies, PosList);
- {disc_only_copies, ram_copies} ->
- mnesia_monitor:unsafe_close_dets(Tab),
- disc_delete_indecies(Tab, Cs, disc_only_copies),
- case InitBy of
- startup ->
- ignore;
+ {{ext,_FromAlias,_FromMod},{ext,ToAlias,ToMod}} ->
+ disc_delete_table(Tab, FromS),
+ ok = ToMod:delete_table(ToAlias, Tab),
+ ok = mnesia_monitor:unsafe_create_external(Tab, ToAlias, ToMod, TabDef),
+ ok = ToMod:load_table(ToAlias, Tab, {dumper,change_table_copy_type}, TabDef),
+ ok = load_from_logfile(ToS, Tab, Logtmp),
+ file:delete(Logtmp),
+ restore_indexes(Tab, ToS, Cs);
+
+ {_,{ext,ToAlias,ToMod}} ->
+ case FromSem of
+ ram_copies ->
+ mnesia_schema:ram_delete_table(Tab, FromS);
_ ->
- mnesia_controller:get_disc_copy(Tab),
- ok
+ if FromSem == disc_copies ->
+ mnesia_schema:ram_delete_table(
+ Tab, FromS);
+ true -> ok
+ end,
+ disc_delete_table(Tab, FromS)
end,
- disc_delete_table(Tab, disc_only_copies);
- {disc_copies, disc_only_copies} ->
- ok = ensure_rename(Dmp, Dat),
- true = open_files(Tab, disc_only_copies, InPlace, InitBy),
- 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),
- ok;
- {disc_only_copies, disc_copies} ->
- mnesia_monitor:unsafe_close_dets(Tab),
- disc_delete_indecies(Tab, Cs, disc_only_copies),
- case InitBy of
- startup ->
- ignore;
- _ ->
- mnesia_log:ets2dcd(Tab),
- mnesia_controller:get_disc_copy(Tab),
- disc_delete_table(Tab, disc_only_copies)
- end
+
+ ok = ToMod:delete_table(ToAlias, Tab),
+ ok = mnesia_monitor:unsafe_create_external(Tab, ToAlias, ToMod, TabDef),
+ ok = ToMod:load_table(ToAlias, Tab, {dumper,change_table_copy_type}, TabDef),
+ ok = load_from_logfile(ToS, Tab, Logtmp),
+ file:delete(Logtmp),
+ restore_indexes(Tab, ToS, Cs);
+
+ {{ext,_FromAlias,_FromMod} = FromS, ToS} ->
+ disc_delete_table(Tab, FromS),
+ case ToS of
+ ram_copies ->
+ change_disc_to_ram(
+ Tab, Cs, FromS, ToS, Logtmp, InitBy);
+ disc_copies ->
+ Args = [{keypos, 2}, public, named_table,
+ Cs#cstruct.type],
+ mnesia_monitor:mktab(Tab, Args),
+ ok = load_from_logfile(ToS, Tab, Logtmp),
+ file:delete(Logtmp);
+ disc_only_copies ->
+ %% ok = ensure_rename(Dmp, Dat),
+ true = open_files(Tab, ToS, InPlace, InitBy),
+ ok = load_from_logfile(ToS, Tab, Logtmp),
+ file:delete(Logtmp)
+ end,
+ restore_indexes(Tab, ToS, Cs);
+
+ _NoneAreExt ->
+
+ case {FromSem, ToSem} of
+ {ram_copies, disc_copies} when Tab == schema ->
+ ok = ensure_rename(Dmp, Dat);
+ {ram_copies, disc_copies} ->
+ file:delete(Dcl),
+ ok = ensure_rename(Dmp, Dcd);
+ {disc_copies, ram_copies} when Tab == schema ->
+ mnesia_lib:set(use_dir, false),
+ mnesia_monitor:unsafe_close_dets(Tab),
+ ok = file:delete(Dat);
+ {disc_copies, ram_copies} ->
+ _ = file:delete(Dcl),
+ _ = file:delete(Dcd),
+ ok;
+ {ram_copies, disc_only_copies} ->
+ ok = ensure_rename(Dmp, Dat),
+ true = open_files(Tab, ToS, InPlace, InitBy),
+ %% ram_delete_table must be done before
+ %% init_indecies, it uses info which is reset
+ %% in init_indecies, it doesn't matter, because
+ %% init_indecies don't use the ram replica of
+ %% the table when creating the disc index;
+ %% Could be improved :)
+ mnesia_schema:ram_delete_table(Tab, FromS),
+ restore_indexes(Tab, ToS, Cs);
+ {disc_only_copies, ram_copies} when FromS == disc_only_copies ->
+ mnesia_monitor:unsafe_close_dets(Tab),
+ disc_delete_indecies(Tab, Cs, FromS),
+ case InitBy of
+ startup ->
+ ignore;
+ _ ->
+ mnesia_controller:get_disc_copy(Tab),
+ ok
+ end,
+ disc_delete_table(Tab, FromS);
+ {disc_only_copies, ram_copies} when element(1, FromS) == ext ->
+ change_disc_to_ram(
+ Tab, Cs, FromS, ToS, Logtmp, InitBy);
+ {disc_copies, disc_only_copies} ->
+ ok = ensure_rename(Dmp, Dat),
+ true = open_files(Tab, ToS, InPlace, InitBy),
+ mnesia_schema:ram_delete_table(Tab, FromS),
+ restore_indexes(Tab, ToS, Cs),
+ _ = 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),
+ case InitBy of
+ startup ->
+ ignore;
+ _ ->
+ mnesia_log:ets2dcd(Tab),
+ mnesia_controller:get_disc_copy(Tab),
+ disc_delete_table(Tab, disc_only_copies)
+ end
+ end
end;
true ->
ignore
@@ -607,6 +698,9 @@ insert_op(Tid, _, {op, restore_recreate, TabDef}, InPlace, InitBy) ->
Tab = Cs#cstruct.name,
Type = Cs#cstruct.type,
Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ Semantics = if Storage==unknown -> unknown;
+ true -> storage_semantics(Storage)
+ end,
%% Delete all possibly existing files and tables
disc_delete_table(Tab, Storage),
disc_delete_indecies(Tab, Cs, Storage),
@@ -625,13 +719,13 @@ insert_op(Tid, _, {op, restore_recreate, TabDef}, InPlace, InitBy) ->
%% And create new ones..
if
- (InitBy == startup) or (Storage == unknown) ->
+ (InitBy == startup) or (Semantics == unknown) ->
ignore;
- Storage == ram_copies ->
+ Semantics == ram_copies ->
EtsProps = proplists:get_value(ets, StorageProps, []),
Args = [{keypos, 2}, public, named_table, Type | EtsProps],
mnesia_monitor:mktab(Tab, Args);
- Storage == disc_copies ->
+ Semantics == disc_copies ->
EtsProps = proplists:get_value(ets, StorageProps, []),
Args = [{keypos, 2}, public, named_table, Type | EtsProps],
mnesia_monitor:mktab(Tab, Args),
@@ -640,7 +734,7 @@ insert_op(Tid, _, {op, restore_recreate, TabDef}, InPlace, InitBy) ->
{repair, false}, {mode, read_write}],
{ok, Log} = mnesia_monitor:open_log(FArg),
mnesia_monitor:unsafe_close_log(Log);
- Storage == disc_only_copies ->
+ Storage == disc_only_copies -> % note: Storage, not Semantics
File = mnesia_lib:tab2dat(Tab),
file:delete(File),
DetsProps = proplists:get_value(dets, StorageProps, []),
@@ -649,7 +743,10 @@ insert_op(Tid, _, {op, restore_recreate, TabDef}, InPlace, InitBy) ->
{keypos, 2},
{repair, mnesia_monitor:get_env(auto_repair)}
| DetsProps ],
- mnesia_monitor:open_dets(Tab, Args)
+ mnesia_monitor:open_dets(Tab, Args);
+ element(1,Storage) == ext ->
+ {ext, Alias, Mod} = Storage,
+ Mod:create_table(Alias, Tab, [])
end,
insert_op(Tid, ignore, {op, create_table, TabDef}, InPlace, InitBy);
@@ -659,9 +756,10 @@ insert_op(Tid, _, {op, create_table, TabDef}, InPlace, InitBy) ->
Tab = Cs#cstruct.name,
Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
StorageProps = Cs#cstruct.storage_properties,
+ Semantics = storage_semantics(Storage),
case InitBy of
startup ->
- case Storage of
+ case Semantics of
unknown ->
ignore;
ram_copies ->
@@ -679,20 +777,10 @@ insert_op(Tid, _, {op, create_table, TabDef}, InPlace, InitBy) ->
read_write),
mnesia_log:unsafe_close_log(temp)
end;
- _ ->
+ disc_only_copies ->
DetsProps = proplists:get_value(dets, StorageProps, []),
- Args = [{file, mnesia_lib:tab2dat(Tab)},
- {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)},
- {keypos, 2},
- {repair, mnesia_monitor:get_env(auto_repair)}
- | DetsProps ],
- case mnesia_monitor:open_dets(Tab, Args) of
- {ok, _} ->
- mnesia_monitor:unsafe_close_dets(Tab);
- {error, Error} ->
- exit({"Failed to create dets table", Error})
- end
+ try_create_disc_only_copy(Storage, Tab, Cs, DetsProps)
end;
_ ->
Copies = mnesia_lib:copy_holders(Cs),
@@ -715,7 +803,7 @@ insert_op(Tid, _, {op, create_table, TabDef}, InPlace, InitBy) ->
false ->
mnesia_lib:set({Tab, where_to_read}, node())
end,
- case Storage of
+ case Semantics of
ram_copies ->
ignore;
_ ->
@@ -838,7 +926,7 @@ insert_op(Tid, _, {op, del_table_copy, Storage, Node, TabDef}, InPlace, InitBy)
insert_cstruct(Tid, Cs, true, InPlace, InitBy);
Tab /= schema ->
mnesia_controller:del_active_replica(Tab, Node),
- mnesia_lib:del({Tab, Storage}, Node),
+ mnesia_lib:del({Tab, storage_alias(Storage)}, Node),
if
Node == node() ->
case Cs#cstruct.local_content of
@@ -896,9 +984,10 @@ insert_op(Tid, _, {op, add_index, Pos, TabDef}, InPlace, InitBy) ->
Cs = mnesia_schema:list2cs(TabDef),
Tab = insert_cstruct(Tid, Cs, true, InPlace, InitBy),
Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ Semantics = storage_semantics(Storage),
case InitBy of
- startup when Storage == disc_only_copies ->
- true = open_files(Tab, Storage, InPlace, InitBy),
+ startup when Semantics == disc_only_copies ->
+ true = open_files(Tab, Semantics, Storage, InPlace, InitBy),
mnesia_index:init_indecies(Tab, Storage, [Pos]);
startup ->
ignore;
@@ -919,6 +1008,8 @@ insert_op(Tid, _, {op, del_index, Pos, TabDef}, InPlace, InitBy) ->
mnesia_index:del_index_table(Tab, Storage, Pos);
startup ->
ignore;
+ _ when element(1, Storage) == ext ->
+ mnesia_index:del_index_table(Tab, Storage, Pos);
_ ->
mnesia_index:del_index_table(Tab, Storage, Pos)
end,
@@ -958,19 +1049,68 @@ insert_op(Tid, _, {op, change_table_frag, _Change, TabDef}, InPlace, InitBy) ->
Cs = mnesia_schema:list2cs(TabDef),
insert_cstruct(Tid, Cs, true, InPlace, InitBy).
-open_files(Tab, Storage, UpdateInPlace, InitBy)
- when Storage /= unknown, Storage /= ram_copies ->
+
+storage_semantics({ext, Alias, Mod}) ->
+ Mod:semantics(Alias, storage);
+storage_semantics(Storage) when is_atom(Storage) ->
+ Storage.
+
+storage_alias({ext, Alias, _}) ->
+ Alias;
+storage_alias(Storage) when is_atom(Storage) ->
+ Storage.
+
+change_disc_to_ram(Tab, Cs, FromS, ToS, Logtmp, InitBy) ->
+ disc_delete_indecies(Tab, Cs, FromS),
+ case InitBy of
+ startup ->
+ ignore;
+ _ ->
+ %% ram table will already have been created
+ Tab = ets:info(Tab, name), %% assertion
+ load_from_logfile(ToS, Tab, Logtmp),
+ PosList = Cs#cstruct.index,
+ mnesia_index:init_indecies(Tab, ToS, PosList)
+ end,
+ disc_delete_table(Tab, FromS).
+
+
+try_create_disc_only_copy({ext,Alias,Mod}, Tab, Cs, _) ->
+ Mod:create_table(Alias, Tab, mnesia_schema:cs2list(Cs));
+try_create_disc_only_copy(disc_only_copies, Tab, Cs, DetsProps) ->
+ Args = [{file, mnesia_lib:tab2dat(Tab)},
+ {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)}
+ | DetsProps],
+ case mnesia_monitor:open_dets(Tab, Args) of
+ {ok, _} ->
+ mnesia_monitor:unsafe_close_dets(Tab);
+ {error, Error} ->
+ exit({"Failed to create dets table", Error})
+ end.
+
+restore_indexes(Tab, ToS, Cs) ->
+ PosList = Cs#cstruct.index,
+ mnesia_index:init_indecies(Tab, ToS, PosList).
+
+
+open_files(Tab, Storage, UpdateInPlace, InitBy) ->
+ open_files(Tab, storage_semantics(Storage), Storage, UpdateInPlace, InitBy).
+
+open_files(Tab, Semantics, Storage, UpdateInPlace, InitBy)
+ when Storage /= unknown, Semantics /= ram_copies ->
case get({?MODULE, Tab}) of
undefined ->
case ?catch_val({Tab, setorbag}) of
{'EXIT', _} ->
false;
Type ->
- case Storage of
- disc_copies when Tab /= schema ->
+ Cs = val({Tab, cstruct}),
+ if Semantics == disc_copies, Tab /= schema ->
Bool = open_disc_copies(Tab, InitBy),
Bool;
- _ ->
+ Storage == disc_only_copies; Tab == schema ->
Props = val({Tab, storage_properties}),
DetsProps = proplists:get_value(dets, Props, []),
Fname = prepare_open(Tab, UpdateInPlace),
@@ -981,6 +1121,12 @@ open_files(Tab, Storage, UpdateInPlace, InitBy)
| DetsProps],
{ok, _} = mnesia_monitor:open_dets(Tab, Args),
put({?MODULE, Tab}, {opened_dumper, dat}),
+ true;
+ element(1, Storage) == ext ->
+ {ext, Alias, Mod} = Storage,
+ Mod:load_table(Alias, Tab, InitBy,
+ mnesia_schema:cs2list(Cs)),
+ put({?MODULE, Tab}, {opened_dumper, ext}),
true
end
end;
@@ -989,7 +1135,7 @@ open_files(Tab, Storage, UpdateInPlace, InitBy)
{opened_dumper, _} ->
true
end;
-open_files(_Tab, _Storage, _UpdateInPlace, _InitBy) ->
+open_files(_Tab, _Semantics, _Storage, _UpdateInPlace, _InitBy) ->
false.
open_disc_copies(Tab, InitBy) ->
@@ -1076,12 +1222,13 @@ close_files(InPlace, Outcome, InitBy, [{{?MODULE, Tab}, already_dumped} | Tail])
close_files(InPlace, Outcome, InitBy, Tail);
close_files(InPlace, Outcome, InitBy, [{{?MODULE, Tab}, {opened_dumper, Type}} | Tail]) ->
erase({?MODULE, Tab}),
- case val({Tab, storage_type}) of
+ Storage = val({Tab, storage_type}),
+ case storage_semantics(Storage) of
disc_only_copies when InitBy /= startup ->
ignore;
- disc_copies when Tab /= schema ->
+ disc_copies when Storage /= unknown, Tab /= schema ->
mnesia_log:close_log({?MODULE,Tab});
- Storage ->
+ _ ->
do_close(InPlace, Outcome, Tab, Type, Storage)
end,
close_files(InPlace, Outcome, InitBy, Tail);
@@ -1093,11 +1240,16 @@ close_files(_, _, _InitBy, []) ->
%% If storage is unknown during close clean up files, this can happen if timing
%% is right and dirty_write conflicts with schema operations.
+do_close(_, _, Tab, ext, {ext,Alias,Mod}) ->
+ Mod:close_table(Alias, Tab);
do_close(_, _, Tab, dcl, unknown) ->
mnesia_log:close_log({?MODULE,Tab}),
file:delete(mnesia_lib:tab2dcl(Tab));
do_close(_, _, Tab, dcl, _) -> %% To be safe, can it happen?
mnesia_log:close_log({?MODULE,Tab});
+do_close(_, _, _Tab, ext, unknown) ->
+ %% Not sure what to do here, but let's not crash
+ ok;
do_close(InPlace, Outcome, Tab, dat, Storage) ->
mnesia_monitor:close_dets(Tab),
@@ -1148,7 +1300,8 @@ temp_set_master_nodes() ->
Tabs = val({schema, local_tables}),
Masters = [{Tab, (val({Tab, disc_copies}) ++
val({Tab, ram_copies}) ++
- val({Tab, disc_only_copies})) -- [node()]}
+ val({Tab, disc_only_copies}) ++
+ external_copies(Tab)) -- [node()]}
|| Tab <- Tabs],
%% UseDir = false since we don't want to remember these
%% masternodes and we are running (really soon anyway) since we want this
@@ -1156,6 +1309,13 @@ temp_set_master_nodes() ->
mnesia_recover:log_master_nodes(Masters, false, yes),
ok.
+external_copies(Tab) ->
+ case ?catch_val({Tab, external_copies}) of
+ {'EXIT',_} -> [];
+ Ext ->
+ lists:concat([Ns || {_, Ns} <- Ext])
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Raw dump of table. Dumper must have unique access to the ets table.
@@ -1207,6 +1367,59 @@ raw_named_dump_table(Tab, Ftype) ->
raw_dump_table(DetsRef, EtsRef) ->
dets:from_ets(DetsRef, EtsRef).
+dump_to_logfile(Storage, Tab) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ Logtmp = mnesia_lib:tab2logtmp(Tab),
+ file:delete(Logtmp),
+ case disk_log:open([{name, make_ref()},
+ {file, Logtmp},
+ {repair, false},
+ {linkto, self()}]) of
+ {ok, Fd} ->
+ mnesia_lib:db_fixtable(Storage, Tab, true),
+ try do_dump_to_logfile(Storage, Tab, Fd)
+ after
+ mnesia_lib:db_fixtable(Storage, Tab, false)
+ end;
+ {error, _} = Error ->
+ Error
+ end;
+ false ->
+ {error, {has_no_disc, node()}}
+ end.
+
+do_dump_to_logfile(Storage, Tab, Fd) ->
+ Pat = [{'_',[],['$_']}],
+ log_terms(mnesia_lib:db_select_init(Storage, Tab, Pat, 100), Storage, Tab, Pat, Fd).
+
+log_terms({Objs, Cont}, Storage, Tab, Pat, Fd) ->
+ ok = disk_log:alog_terms(Fd, Objs),
+ log_terms(mnesia_lib:db_select_cont(Storage, Cont, '_'), Storage, Tab, Pat, Fd);
+log_terms('$end_of_table', _, _, _, Fd) ->
+ disk_log:close(Fd).
+
+load_from_logfile(Storage, Tab, F) ->
+ case disk_log:open([{name, make_ref()},
+ {file, F},
+ {repair, true},
+ {linkto, self()}]) of
+ {ok, Fd} ->
+ chunk_from_log(disk_log:chunk(Fd, start), Fd, Storage, Tab);
+ {repaired, Fd, _, _} ->
+ chunk_from_log(disk_log:chunk(Fd, start), Fd, Storage, Tab);
+ {error, _} = E ->
+ E
+ end.
+
+chunk_from_log({Cont, Terms}, Fd, Storage, Tab) ->
+ _ = [mnesia_lib:db_put(Storage, Tab, T) || T <- Terms],
+ chunk_from_log(disk_log:chunk(Fd, Cont), Fd, Storage, Tab);
+chunk_from_log(eof, _, _, _) ->
+ ok.
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Load regulator
%%
diff --git a/lib/mnesia/src/mnesia_ext_sup.erl b/lib/mnesia/src/mnesia_ext_sup.erl
new file mode 100644
index 0000000000..3e6c72161c
--- /dev/null
+++ b/lib/mnesia/src/mnesia_ext_sup.erl
@@ -0,0 +1,59 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% This module implements a supervisor for external (plug-in) processes
+
+-module(mnesia_ext_sup).
+-behaviour(supervisor).
+
+-export([start/0,
+ init/1,
+ start_proc/4, start_proc/5,
+ stop_proc/1]).
+
+-define(SHUTDOWN, 120000). % 2 minutes
+
+start() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+start_proc(Name, M, F, A) ->
+ start_proc(Name, M, F, A, []).
+
+start_proc(Name, M, F, A, Opts) ->
+ [Restart, Shutdown, Type, Modules] =
+ [proplists:get_value(K, Opts, Default)
+ || {K, Default} <- [{restart, transient},
+ {shutdown, ?SHUTDOWN},
+ {type, worker},
+ {modules, [M]}]],
+ case supervisor:start_child(
+ ?MODULE, {Name, {M,F,A}, Restart, Shutdown, Type, Modules}) of
+ {error, already_present} ->
+ supervisor:restart_child(?MODULE, Name);
+ Other ->
+ Other
+ end.
+
+stop_proc(Name) ->
+ supervisor:terminate_child(?MODULE, Name).
+
+init(_) ->
+ {ok, {{one_for_all, 10, 60}, []}}.
diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl
index c4de603460..c6e812b36d 100644
--- a/lib/mnesia/src/mnesia_frag.erl
+++ b/lib/mnesia/src/mnesia_frag.erl
@@ -183,6 +183,8 @@ table_info2(ActivityId, Opaque, Tab, Frag, Item) ->
length(val({Tab, disc_copies}));
n_disc_only_copies ->
length(val({Tab, disc_only_copies}));
+ n_external_copies ->
+ length(val({Tab, external_copies}));
frag_names ->
frag_names(Tab);
@@ -498,13 +500,13 @@ expand_cstruct(Cs, Mode) ->
%% Verify keys
ValidKeys = [foreign_key, n_fragments, node_pool,
n_ram_copies, n_disc_copies, n_disc_only_copies,
- hash_module, hash_state],
+ n_external_copies, hash_module, hash_state],
Keys = mnesia_schema:check_keys(Tab, Props, ValidKeys),
mnesia_schema:check_duplicates(Tab, Keys),
%% Pick fragmentation props
ForeignKey = mnesia_schema:pick(Tab, foreign_key, Props, undefined),
- {ForeignKey2, N, Pool, DefaultNR, DefaultND, DefaultNDO} =
+ {ForeignKey2, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt} =
pick_props(Tab, Cs, ForeignKey),
%% Verify node_pool
@@ -518,6 +520,7 @@ expand_cstruct(Cs, Mode) ->
NR = mnesia_schema:pick(Tab, n_ram_copies, Props, 0),
ND = mnesia_schema:pick(Tab, n_disc_copies, Props, 0),
NDO = mnesia_schema:pick(Tab, n_disc_only_copies, Props, 0),
+ NExt = mnesia_schema:pick(Tab, n_external_copies, Props, 0),
PosInt = fun(I) when is_integer(I), I >= 0 -> true;
(_I) -> false
@@ -528,6 +531,8 @@ expand_cstruct(Cs, Mode) ->
{bad_type, Tab, {n_disc_copies, ND}}),
mnesia_schema:verify(true, PosInt(NDO),
{bad_type, Tab, {n_disc_only_copies, NDO}}),
+ mnesia_schema:verify(true, PosInt(NExt),
+ {bad_type, Tab, {n_external_copies, NDO}}),
%% Verify n_fragments
Cs2 = verify_n_fragments(N, Cs, Mode),
@@ -542,13 +547,13 @@ expand_cstruct(Cs, Mode) ->
hash_module = HashMod,
hash_state = HashState2},
if
- NR == 0, ND == 0, NDO == 0 ->
- do_expand_cstruct(Cs2, FH, N, Pool, DefaultNR, DefaultND, DefaultNDO, Mode);
+ NR == 0, ND == 0, NDO == 0, NExt == 0 ->
+ do_expand_cstruct(Cs2, FH, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt, Mode);
true ->
- do_expand_cstruct(Cs2, FH, N, Pool, NR, ND, NDO, Mode)
+ do_expand_cstruct(Cs2, FH, N, Pool, NR, ND, NDO, NExt, Mode)
end.
-do_expand_cstruct(Cs, FH, N, Pool, NR, ND, NDO, Mode) ->
+do_expand_cstruct(Cs, FH, N, Pool, NR, ND, NDO, NExt, Mode) ->
Tab = Cs#cstruct.name,
LC = Cs#cstruct.local_content,
@@ -562,14 +567,15 @@ do_expand_cstruct(Cs, FH, N, Pool, NR, ND, NDO, Mode) ->
%% Add empty fragments
CommonProps = [{base_table, Tab}],
Cs2 = Cs#cstruct{frag_properties = lists:sort(CommonProps)},
- expand_frag_cstructs(N, NR, ND, NDO, Cs2, Pool, Pool, FH, Mode).
+ expand_frag_cstructs(N, NR, ND, NDO, NExt, Cs2, Pool, Pool, FH, Mode).
verify_n_fragments(N, Cs, Mode) when is_integer(N), N >= 1 ->
case Mode of
create ->
Cs#cstruct{ram_copies = [],
disc_copies = [],
- disc_only_copies = []};
+ disc_only_copies = [],
+ external_copies = []};
activate ->
Reason = {combine_error, Cs#cstruct.name, {n_fragments, N}},
mnesia_schema:verify(1, N, Reason),
@@ -607,7 +613,8 @@ pick_props(Tab, Cs, {ForeignTab, Attr}) ->
DefaultNR = length(val({ForeignTab, ram_copies})),
DefaultND = length(val({ForeignTab, disc_copies})),
DefaultNDO = length(val({ForeignTab, disc_only_copies})),
- {Key, N, Pool, DefaultNR, DefaultND, DefaultNDO};
+ DefaultNExt = length(val({ForeignTab, external_copies})),
+ {Key, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt};
pick_props(Tab, Cs, undefined) ->
Props = Cs#cstruct.frag_properties,
DefaultN = 1,
@@ -617,22 +624,23 @@ pick_props(Tab, Cs, undefined) ->
DefaultNR = 1,
DefaultND = 0,
DefaultNDO = 0,
- {undefined, N, Pool, DefaultNR, DefaultND, DefaultNDO};
+ DefaultNExt = 0,
+ {undefined, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt};
pick_props(Tab, _Cs, BadKey) ->
mnesia:abort({bad_type, Tab, {foreign_key, BadKey}}).
-expand_frag_cstructs(N, NR, ND, NDO, CommonCs, Dist, Pool, FH, Mode)
+expand_frag_cstructs(N, NR, ND, NDO, NExt, CommonCs, Dist, Pool, FH, Mode)
when N > 1, Mode == create ->
Frag = n_to_frag_name(CommonCs#cstruct.name, N),
Cs = CommonCs#cstruct{name = Frag},
- {Cs2, RevModDist, RestDist} = set_frag_nodes(NR, ND, NDO, Cs, Dist, []),
+ {Cs2, RevModDist, RestDist} = set_frag_nodes(NR, ND, NDO, NExt, Cs, Dist, []),
ModDist = lists:reverse(RevModDist),
Dist2 = rearrange_dist(Cs, ModDist, RestDist, Pool),
%% Adjusts backwards, but it doesn't matter.
{FH2, _FromFrags, _AdditionalWriteFrags} = adjust_before_split(FH),
- CsList = expand_frag_cstructs(N - 1, NR, ND, NDO, CommonCs, Dist2, Pool, FH2, Mode),
+ CsList = expand_frag_cstructs(N - 1, NR, ND, NDO, NExt, CommonCs, Dist2, Pool, FH2, Mode),
[Cs2 | CsList];
-expand_frag_cstructs(1, NR, ND, NDO, CommonCs, Dist, Pool, FH, Mode) ->
+expand_frag_cstructs(1, NR, ND, NDO, NExt, CommonCs, Dist, Pool, FH, Mode) ->
BaseProps = CommonCs#cstruct.frag_properties ++
[{foreign_key, FH#frag_state.foreign_key},
{hash_module, FH#frag_state.hash_module},
@@ -645,25 +653,29 @@ expand_frag_cstructs(1, NR, ND, NDO, CommonCs, Dist, Pool, FH, Mode) ->
activate ->
[BaseCs];
create ->
- {BaseCs2, _, _} = set_frag_nodes(NR, ND, NDO, BaseCs, Dist, []),
+ {BaseCs2, _, _} = set_frag_nodes(NR, ND, NDO, NExt, BaseCs, Dist, []),
[BaseCs2]
end.
-set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when NR > 0 ->
+set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when NR > 0 ->
Pos = #cstruct.ram_copies,
{Cs2, Head2} = set_frag_node(Cs, Pos, Head),
- set_frag_nodes(NR - 1, ND, NDO, Cs2, Tail, [Head2 | Acc]);
-set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when ND > 0 ->
+ set_frag_nodes(NR - 1, ND, NDO, NExt, Cs2, Tail, [Head2 | Acc]);
+set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when ND > 0 ->
Pos = #cstruct.disc_copies,
{Cs2, Head2} = set_frag_node(Cs, Pos, Head),
- set_frag_nodes(NR, ND - 1, NDO, Cs2, Tail, [Head2 | Acc]);
-set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when NDO > 0 ->
+ set_frag_nodes(NR, ND - 1, NDO, NExt, Cs2, Tail, [Head2 | Acc]);
+set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when NDO > 0 ->
Pos = #cstruct.disc_only_copies,
{Cs2, Head2} = set_frag_node(Cs, Pos, Head),
- set_frag_nodes(NR, ND, NDO - 1, Cs2, Tail, [Head2 | Acc]);
-set_frag_nodes(0, 0, 0, Cs, RestDist, ModDist) ->
+ set_frag_nodes(NR, ND, NDO - 1, NExt, Cs2, Tail, [Head2 | Acc]);
+set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when NExt > 0 ->
+ Pos = #cstruct.external_copies,
+ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
+ set_frag_nodes(NR, ND, NDO, NExt - 1, Cs2, Tail, [Head2 | Acc]);
+set_frag_nodes(0, 0, 0, 0, Cs, RestDist, ModDist) ->
{Cs, ModDist, RestDist};
-set_frag_nodes(_, _, _, Cs, [], _) ->
+set_frag_nodes(_, _, _, _, Cs, [], _) ->
mnesia:abort({combine_error, Cs#cstruct.name, "Too few nodes in node_pool"}).
set_frag_node(Cs, Pos, Head) ->
@@ -840,13 +852,15 @@ make_add_frag(Tab, SortedNs) ->
NR = length(Cs#cstruct.ram_copies),
ND = length(Cs#cstruct.disc_copies),
NDO = length(Cs#cstruct.disc_only_copies),
+ NExt = length(Cs#cstruct.external_copies),
NewCs = Cs#cstruct{name = NewFrag,
frag_properties = [{base_table, Tab}],
ram_copies = [],
disc_copies = [],
- disc_only_copies = []},
+ disc_only_copies = [],
+ external_copies = []},
- {NewCs2, _, _} = set_frag_nodes(NR, ND, NDO, NewCs, SortedNs, []),
+ {NewCs2, _, _} = set_frag_nodes(NR, ND, NDO, NExt, NewCs, SortedNs, []),
[NewOp] = mnesia_schema:make_create_table(NewCs2),
SplitOps = split(Tab, FH2, FromIndecies, FragNames, []),
@@ -1319,7 +1333,8 @@ count_frag([Frag | Frags], Dist) ->
Dist2 = incr_nodes(val({Frag, ram_copies}), Dist),
Dist3 = incr_nodes(val({Frag, disc_copies}), Dist2),
Dist4 = incr_nodes(val({Frag, disc_only_copies}), Dist3),
- count_frag(Frags, Dist4);
+ Dist5 = incr_nodes(val({Frag, external_copies}), Dist4),
+ count_frag(Frags, Dist5);
count_frag([], Dist) ->
Dist.
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index be05cfbea3..73d170d1fa 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -23,8 +23,8 @@
-module(mnesia_index).
-export([read/5,
- add_index/5,
- delete_index/3,
+ add_index/6,
+ delete_index/4,
del_object_index/5,
clear_index/4,
dirty_match_object/3,
@@ -39,19 +39,21 @@
get_index_table/3,
tab2filename/2,
- tab2tmp_filename/2,
init_index/2,
init_indecies/3,
del_transient/2,
del_transient/3,
- del_index_table/3]).
+ del_index_table/3,
+
+ index_info/2,
+ ext_index_instances/1]).
-import(mnesia_lib, [val/1, verbose/2]).
-include("mnesia.hrl").
-record(index, {setorbag, pos_list}).
-%% read an object list throuh its index table
+%% read an object list through its index table
%% we assume that table Tab has index on attribute number Pos
read(Tid, Store, Tab, IxKey, Pos) ->
@@ -64,69 +66,103 @@ read(Tid, Store, Tab, IxKey, Pos) ->
ResList
end.
-add_index(Index, Tab, Key, Obj, Old) ->
- add_index2(Index#index.pos_list, Index#index.setorbag, Tab, Key, Obj, Old).
-
-add_index2([{Pos, Ixt} |Tail], bag, Tab, K, Obj, OldRecs) ->
- db_put(Ixt, {element(Pos, Obj), K}),
- add_index2(Tail, bag, Tab, K, Obj, OldRecs);
-add_index2([{Pos, Ixt} |Tail], Type, Tab, K, Obj, OldRecs0) ->
+ext_index_instances(Tab) ->
+ #index{pos_list = PosL} = val({Tab, index_info}),
+ lists:foldr(
+ fun({_, {{ext,Alias,Mod}, Tag}}, Acc) ->
+ [{Alias, Mod, Tag}|Acc];
+ (_, Acc) ->
+ Acc
+ end, [], PosL).
+
+
+add_index(#index{pos_list = PosL, setorbag = SorB},
+ Storage, Tab, Key, Obj, Old) ->
+ add_index2(PosL, SorB, Storage, Tab, Key, Obj, Old).
+
+add_index2([{{Pos,Type}, Ixt} |Tail], bag, Storage, Tab, K, Obj, OldRecs) ->
+ ValsF = index_vals_f(Storage, Tab, Pos),
+ Vals = ValsF(Obj),
+ put_index_vals(Type, Ixt, Vals, K),
+ add_index2(Tail, bag, Storage, Tab, K, Obj, OldRecs);
+add_index2([{{Pos, Type}, Ixt} |Tail], SorB, Storage, Tab, K, Obj, OldRecs0) ->
%% Remove old tuples in index if Tab is updated
+ ValsF = index_vals_f(Storage, Tab, Pos), NewVals = ValsF(Obj),
OldRecs1 = case OldRecs0 of
- undefined -> mnesia_lib:db_get(Tab, K);
+ undefined -> mnesia_lib:db_get(Storage, Tab, K);
_ -> OldRecs0
end,
- IdxVal = element(Pos, Obj),
- case [Old || Old <- OldRecs1, element(Pos, Old) =/= IdxVal] of
- [] when OldRecs1 =:= [] -> %% Write
- db_put(Ixt, {element(Pos, Obj), K}),
- add_index2(Tail, Type, Tab, K, Obj, OldRecs0);
+ IdxVal = ValsF(Obj),
+ case [Old || Old <- OldRecs1, ValsF(Old) =/= IdxVal] of
+ [] when OldRecs1 =:= [] -> % Write
+ put_index_vals(Type, Ixt, NewVals, K),
+ add_index2(Tail, SorB, Storage, Tab, K, Obj, OldRecs1);
[] -> %% when OldRecs1 =/= [] Update without modifying index field
- add_index2(Tail, Type, Tab, K, Obj, OldRecs0);
+ add_index2(Tail, SorB, Storage, Tab, K, Obj, OldRecs1);
OldRecs -> %% Update
- db_put(Ixt, {element(Pos, Obj), K}),
- del_ixes(Ixt, OldRecs, Pos, K),
- add_index2(Tail, Type, Tab, K, Obj, OldRecs0)
+ put_index_vals(Type, Ixt, NewVals, K),
+ [del_ixes(Type, Ixt, ValsF, OldObj, K) || OldObj <- OldRecs],
+ add_index2(Tail, SorB, Storage, Tab, K, Obj, OldRecs1)
end;
-add_index2([], _, _Tab, _K, _Obj, _) -> ok.
+add_index2([], _, _, _Tab, _K, _Obj, _) -> ok.
+
+delete_index(Index, Storage, Tab, K) ->
+ delete_index2(Index#index.pos_list, Storage, Tab, K).
-delete_index(Index, Tab, K) ->
- delete_index2(Index#index.pos_list, Tab, K).
+delete_index2([{{Pos, Type}, Ixt} | Tail], Storage, Tab, K) ->
+ DelObjs = mnesia_lib:db_get(Storage, Tab, K),
+ ValsF = index_vals_f(Storage, Tab, Pos),
+ [del_ixes(Type, Ixt, ValsF, Obj, K) || Obj <- DelObjs],
+ delete_index2(Tail, Storage, Tab, K);
+delete_index2([], _Storage, _Tab, _K) -> ok.
-delete_index2([{Pos, Ixt} | Tail], Tab, K) ->
- DelObjs = mnesia_lib:db_get(Tab, K),
- del_ixes(Ixt, DelObjs, Pos, K),
- delete_index2(Tail, Tab, K);
-delete_index2([], _Tab, _K) -> ok.
+put_index_vals(ordered, Ixt, Vals, K) ->
+ [db_put(Ixt, {{V, K}}) || V <- Vals];
+put_index_vals(bag, Ixt, Vals, K) ->
+ [db_put(Ixt, {V, K}) || V <- Vals].
-del_ixes(_Ixt, [], _Pos, _L) -> ok;
-del_ixes(Ixt, [Obj | Tail], Pos, Key) ->
- db_match_erase(Ixt, {element(Pos, Obj), Key}),
- del_ixes(Ixt, Tail, Pos, Key).
+del_ixes(bag, Ixt, ValsF, Obj, Key) ->
+ Vals = ValsF(Obj),
+ [db_match_erase(Ixt, {V, Key}) || V <- Vals];
+del_ixes(ordered, Ixt, ValsF, Obj, Key) ->
+ Vals = ValsF(Obj),
+ [db_erase(Ixt, {V,Key}) || V <- Vals].
-del_object_index(Index, Tab, K, Obj, Old) ->
- del_object_index2(Index#index.pos_list, Index#index.setorbag, Tab, K, Obj, Old).
+del_object_index(#index{pos_list = PosL, setorbag = SorB}, Storage, Tab, K, Obj) ->
+ del_object_index2(PosL, SorB, Storage, Tab, K, Obj).
-del_object_index2([], _, _Tab, _K, _Obj, _Old) -> ok;
-del_object_index2([{Pos, Ixt} | Tail], SoB, Tab, K, Obj, Old) ->
+del_object_index2([], _, _Storage, _Tab, _K, _Obj) -> ok;
+del_object_index2([{{Pos, Type}, Ixt} | Tail], SoB, Storage, Tab, K, Obj) ->
+ ValsF = index_vals_f(Storage, Tab, Pos),
case SoB of
bag ->
- del_object_bag(Tab, K, Obj, Pos, Ixt, Old);
+ del_object_bag(Type, ValsF, Tab, K, Obj, Ixt);
_ -> %% If set remove the tuple in index table
- del_ixes(Ixt, [Obj], Pos, K)
+ del_ixes(Type, Ixt, ValsF, Obj, K)
end,
- del_object_index2(Tail, SoB, Tab, K, Obj, Old).
-
-del_object_bag(Tab, Key, Obj, Pos, Ixt, undefined) ->
- IxKey = element(Pos, Obj),
- Old = [X || X <- mnesia_lib:db_get(Tab, Key), element(Pos, X) =:= IxKey],
- del_object_bag(Tab, Key, Obj, Pos, Ixt, Old);
-%% If Tab type is bag we need remove index identifier if the object being
-%% deleted was the last one
-del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when Old =:= [Obj] ->
- del_ixes(Ixt, [Obj], Pos, Key);
-del_object_bag(_Tab, _Key, _Obj, _Pos, _Ixt, _Old) -> ok.
+ del_object_index2(Tail, SoB, Storage, Tab, K, Obj).
+
+del_object_bag(Type, ValsF, Tab, Key, Obj, Ixt) ->
+ IxKeys = ValsF(Obj),
+ Found = [{X, ValsF(X)} || X <- mnesia_lib:db_get(Tab, Key)],
+ del_object_bag_(IxKeys, Found, Type, Tab, Key, Obj, Ixt).
+
+del_object_bag_([IxK|IxKs], Found, Type, Tab, Key, Obj, Ixt) ->
+ case [X || {X, Ixes} <- Found, lists:member(IxK, Ixes)] of
+ [Old] when Old =:= Obj ->
+ case Type of
+ bag ->
+ db_match_erase(Ixt, {IxK, Key});
+ ordered ->
+ db_erase(Ixt, {{IxK, Key}})
+ end;
+ _ ->
+ ok
+ end,
+ del_object_bag_(IxKs, Found, Type, Tab, Key, Obj, Ixt);
+del_object_bag_([], _, _, _, _, _, _) ->
+ ok.
clear_index(Index, Tab, K, Obj) ->
clear_index2(Index#index.pos_list, Tab, K, Obj).
@@ -138,8 +174,9 @@ clear_index2([{_Pos, Ixt} | Tail], Tab, K, Obj) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-dirty_match_object(Tab, Pat, Pos) ->
+dirty_match_object(Tab, Pat, Pos) when is_integer(Pos) ->
%% Assume that we are on the node where the replica is
+ %% Cannot use index plugins here, as they don't map to match patterns
case element(2, Pat) of
'_' ->
IxKey = element(Pos, Pat),
@@ -161,7 +198,7 @@ realkeys(Tab, Pos, IxKey) ->
Index = get_index_table(Tab, Pos),
db_get(Index, IxKey). % a list on the form [{IxKey, RealKey1} , ....
-dirty_select(Tab, Spec, Pos) ->
+dirty_select(Tab, Spec, Pos) when is_integer(Pos) ->
%% Assume that we are on the node where the replica is
%% Returns the records without applying the match spec
%% The actual filtering is handled by the caller
@@ -171,59 +208,80 @@ dirty_select(Tab, Spec, Pos) ->
lists:append([mnesia_lib:db_get(StorageType, Tab, Key) || {_,Key} <- RealKeys]).
dirty_read(Tab, IxKey, Pos) ->
- ResList = mnesia:dirty_rpc(Tab, ?MODULE, dirty_read2,
- [Tab, IxKey, Pos]),
- case val({Tab, setorbag}) of
- bag ->
- %% Remove all tuples which don't include Ixkey
- mnesia_lib:key_search_all(IxKey, Pos, ResList);
- _ ->
- ResList
- end.
+ mnesia:dirty_rpc(Tab, ?MODULE, dirty_read2,
+ [Tab, IxKey, Pos]).
dirty_read2(Tab, IxKey, Pos) ->
- Ix = get_index_table(Tab, Pos),
- Keys = db_match(Ix, {IxKey, '$1'}),
- r_keys(Keys, Tab, []).
-
-r_keys([[H]|T],Tab,Ack) ->
- V = mnesia_lib:db_get(Tab, H),
- r_keys(T, Tab, V ++ Ack);
-r_keys([], _, Ack) ->
- Ack.
+ #index{pos_list = PosL} = val({Tab, index_info}),
+ Storage = val({Tab, storage_type}),
+ {Type, Ixt} = pick_index(PosL, Tab, Pos),
+ Pat = case Type of
+ ordered -> [{{{IxKey, '$1'}}, [], ['$1']}];
+ bag -> [{{IxKey, '$1'}, [], ['$1']}]
+ end,
+ Keys = db_select(Ixt, Pat),
+ ValsF = index_vals_f(Storage, Tab, Pos),
+ lists:reverse(
+ lists:foldl(
+ fun(K, Acc) ->
+ lists:foldl(
+ fun(Obj, Acc1) ->
+ case lists:member(IxKey, ValsF(Obj)) of
+ true -> [Obj|Acc1];
+ false -> Acc1
+ end
+ end, Acc, mnesia_lib:db_get(Storage, Tab, K))
+ end, [], Keys)).
+
+pick_index([{{{Pfx,_},IxType}, Ixt}|_], _Tab, {_} = Pfx) ->
+ {IxType, Ixt};
+pick_index([{{Pos,IxType}, Ixt}|_], _Tab, Pos) ->
+ {IxType, Ixt};
+pick_index([_|T], Tab, Pos) ->
+ pick_index(T, Tab, Pos);
+pick_index([], Tab, Pos) ->
+ mnesia:abort({no_exist, Tab, {index, Pos}}).
+
%%%%%%% Creation, Init and deletion routines for index tables
%% We can have several indexes on the same table
%% this can be a fairly costly operation if table is *very* large
-tab2filename(Tab, Pos) ->
+tab2filename(Tab, {A}) when is_atom(A) ->
+ mnesia_lib:dir(Tab) ++ "_-" ++ atom_to_list(A) ++ "-.DAT";
+tab2filename(Tab, T) when is_tuple(T) ->
+ tab2filename(Tab, element(1, T));
+tab2filename(Tab, Pos) when is_integer(Pos) ->
mnesia_lib:dir(Tab) ++ "_" ++ integer_to_list(Pos) ++ ".DAT".
-tab2tmp_filename(Tab, Pos) ->
- mnesia_lib:dir(Tab) ++ "_" ++ integer_to_list(Pos) ++ ".TMP".
-
init_index(Tab, Storage) ->
- PosList = val({Tab, index}),
+ Cs = val({Tab, cstruct}),
+ PosList = Cs#cstruct.index,
init_indecies(Tab, Storage, PosList).
init_indecies(Tab, Storage, PosList) ->
case Storage of
unknown ->
ignore;
+ {ext, Alias, Mod} ->
+ init_ext_index(Tab, Storage, Alias, Mod, PosList);
disc_only_copies ->
- init_disc_index(Tab, PosList);
+ init_disc_index(Tab, Storage, PosList);
ram_copies ->
- make_ram_index(Tab, PosList);
+ make_ram_index(Tab, Storage, PosList);
disc_copies ->
- make_ram_index(Tab, PosList)
+ make_ram_index(Tab, Storage, PosList)
end.
%% works for both ram and disc indexes
del_index_table(_, unknown, _) ->
ignore;
-del_index_table(Tab, Storage, Pos) ->
+del_index_table(Tab, Storage, {_} = Pos) ->
+ delete_transient_index(Tab, Pos, Storage),
+ mnesia_lib:del({Tab, index}, Pos);
+del_index_table(Tab, Storage, Pos) when is_integer(Pos) ->
delete_transient_index(Tab, Pos, Storage),
mnesia_lib:del({Tab, index}, Pos).
@@ -236,13 +294,24 @@ del_transient(Tab, [Pos | Tail], Storage) ->
delete_transient_index(Tab, Pos, Storage),
del_transient(Tab, Tail, Storage).
+delete_transient_index(Tab, Pos, {ext, Alias, Mod}) ->
+ PosInfo = case Pos of
+ _ when is_integer(Pos) ->
+ Cs = val({Tab, cstruct}),
+ lists:keyfind(Pos, 1, Cs#cstruct.index);
+ {P, T} -> {P, T}
+ end,
+ Tag = {Tab, index, PosInfo},
+ Mod:close_table(Alias, Tag),
+ Mod:delete_table(Alias, Tag),
+ del_index_info(Tab, Pos),
+ mnesia_lib:unset({Tab, {index, Pos}});
delete_transient_index(Tab, Pos, disc_only_copies) ->
Tag = {Tab, index, Pos},
mnesia_monitor:unsafe_close_dets(Tag),
_ = file:delete(tab2filename(Tab, Pos)),
del_index_info(Tab, Pos), %% Uses val(..)
mnesia_lib:unset({Tab, {index, Pos}});
-
delete_transient_index(Tab, Pos, _Storage) ->
Ixt = val({Tab, {index, Pos}}),
?ets_delete_table(Ixt),
@@ -252,11 +321,12 @@ delete_transient_index(Tab, Pos, _Storage) ->
%%%%% misc functions for the index create/init/delete functions above
%% assuming that the file exists.
-init_disc_index(_Tab, []) ->
+init_disc_index(_Tab, _Storage, []) ->
done;
-init_disc_index(Tab, [Pos | Tail]) when is_integer(Pos) ->
+init_disc_index(Tab, disc_only_copies, [{Pos,_Pref} | Tail]) ->
+ PosInfo = {Pos, bag},
Fn = tab2filename(Tab, Pos),
- IxTag = {Tab, index, Pos},
+ IxTag = {Tab, index, PosInfo},
_ = file:delete(Fn),
Args = [{file, Fn}, {keypos, 1}, {type, bag}],
mnesia_monitor:open_dets(IxTag, Args),
@@ -269,16 +339,59 @@ init_disc_index(Tab, [Pos | Tail]) when is_integer(Pos) ->
mnesia_lib:db_fixtable(Storage, Tab, true),
ok = dets:init_table(IxTag, create_fun(Init, Tab, Pos)),
mnesia_lib:db_fixtable(Storage, Tab, false),
- mnesia_lib:set({Tab, {index, Pos}}, IxTag),
- add_index_info(Tab, val({Tab, setorbag}), {Pos, {dets, IxTag}}),
- init_disc_index(Tab, Tail).
+ mnesia_lib:set({Tab, {index, PosInfo}}, IxTag),
+ add_index_info(Tab, val({Tab, setorbag}), {PosInfo, {dets, IxTag}}),
+ init_disc_index(Tab, Storage, Tail).
+
+init_ext_index(_, _, _, _, []) ->
+ done;
+init_ext_index(Tab, Storage, Alias, Mod, [{Pos,Type} | Tail]) ->
+ PosInfo = {Pos, Type},
+ IxTag = {Tab, index, PosInfo},
+ CS = val({Tab, cstruct}),
+ CsList = mnesia_schema:cs2list(CS),
+ _Res = mnesia_monitor:unsafe_create_external(IxTag, Alias, Mod, CsList),
+ Mod:load_table(Alias, IxTag, init_index, CsList),
+ case Mod:is_index_consistent(Alias, IxTag) of
+ false ->
+ Mod:index_is_consistent(Alias, IxTag, false),
+ Mod:match_delete(Alias, IxTag, '_'),
+ IxValsF = index_vals_f(Storage, Tab, Pos),
+ IxObjF = case Type of
+ bag -> fun(IxVal, Key) -> {IxVal, Key} end;
+ ordered -> fun(IxVal, Key) -> {{IxVal, Key}} end
+ end,
+ mnesia_lib:db_fixtable(Storage, Tab, true),
+ mnesia_lib:db_foldl(
+ Storage,
+ fun(Rec, Acc) ->
+ Key = element(2, Rec),
+ lists:foreach(
+ fun(V) ->
+ IxObj = IxObjF(V, Key),
+ Mod:insert(Alias, IxTag, IxObj)
+ end, IxValsF(Rec)),
+ Acc
+ end, ok, Tab,
+ [{'_', [], ['$_']}], 100),
+ Mod:index_is_consistent(Alias, IxTag, true);
+ true ->
+ ignore
+ end,
+
+ mnesia_lib:set({Tab, {index, PosInfo}}, IxTag),
+
+ add_index_info(Tab, val({Tab, setorbag}), {PosInfo, {Storage, IxTag}}),
+ init_ext_index(Tab, Storage, Alias, Mod, Tail).
create_fun(Cont, Tab, Pos) ->
+ IxF = index_vals_f(disc_only_copies, Tab, Pos),
fun(read) ->
Data =
case Cont of
{start, KeysPerChunk} ->
- mnesia_lib:db_init_chunk(disc_only_copies, Tab, KeysPerChunk);
+ mnesia_lib:db_init_chunk(
+ disc_only_copies, Tab, KeysPerChunk);
'$end_of_table' ->
'$end_of_table';
_Else ->
@@ -288,56 +401,82 @@ create_fun(Cont, Tab, Pos) ->
'$end_of_table' ->
end_of_input;
{Recs, Next} ->
- IdxElems = [{element(Pos, Obj), element(2, Obj)} || Obj <- Recs],
+ IdxElems = lists:flatmap(
+ fun(Obj) ->
+ PrimK = element(2, Obj),
+ [{V, PrimK} || V <- IxF(Obj)]
+ end, Recs),
{IdxElems, create_fun(Next, Tab, Pos)}
end;
(close) ->
ok
end.
-make_ram_index(_, []) ->
+make_ram_index(_, _, []) ->
done;
-make_ram_index(Tab, [Pos | Tail]) ->
- add_ram_index(Tab, Pos),
- make_ram_index(Tab, Tail).
+make_ram_index(Tab, Storage, [Pos | Tail]) ->
+ add_ram_index(Tab, Storage, Pos),
+ make_ram_index(Tab, Storage, Tail).
-add_ram_index(Tab, Pos) when is_integer(Pos) ->
- verbose("Creating index for ~w ~n", [Tab]),
+add_ram_index(Tab, Storage, {Pos, _Pref}) ->
+ Type = ordered,
+ verbose("Creating index for ~w ~p ~p~n", [Tab, Pos, Type]),
SetOrBag = val({Tab, setorbag}),
- IndexType = case SetOrBag of
- set -> duplicate_bag;
- ordered_set -> duplicate_bag;
- bag -> bag
- end,
- Index = mnesia_monitor:mktab(mnesia_index, [IndexType, public]),
+ IxValsF = index_vals_f(Storage, Tab, Pos),
+ IxFun = fun(Val, Key) -> {{Val, Key}} end,
+ Index = mnesia_monitor:mktab(mnesia_index, [ordered_set, public]),
Insert = fun(Rec, _Acc) ->
- true = ?ets_insert(Index, {element(Pos, Rec), element(2, Rec)})
+ PrimK = element(2, Rec),
+ true = ?ets_insert(
+ Index, [IxFun(V, PrimK)
+ || V <- IxValsF(Rec)])
end,
mnesia_lib:db_fixtable(ram_copies, Tab, true),
- true = ets:foldl(Insert, true, Tab),
+ true = mnesia_lib:db_foldl(Storage, Insert, true, Tab),
mnesia_lib:db_fixtable(ram_copies, Tab, false),
mnesia_lib:set({Tab, {index, Pos}}, Index),
- add_index_info(Tab, SetOrBag, {Pos, {ram, Index}});
-add_ram_index(_Tab, snmp) ->
+ add_index_info(Tab, SetOrBag, {{Pos, Type}, {ram, Index}});
+add_ram_index(_Tab, _, snmp) ->
ok.
-add_index_info(Tab, Type, IxElem) ->
+index_info(SetOrBag, PosList) ->
+ IxPlugins = mnesia_schema:index_plugins(),
+ ExpPosList = lists:map(
+ fun({{P,Type},Ixt} = PI) ->
+ case P of
+ {_} = IxN ->
+ {_, M, F} =
+ lists:keyfind(IxN, 1, IxPlugins),
+ {{{IxN,M,F}, Type}, Ixt};
+ _ ->
+ PI
+ end
+ end, PosList),
+ #index{setorbag = SetOrBag, pos_list = ExpPosList}.
+
+add_index_info(Tab, SetOrBag, IxElem) ->
Commit = val({Tab, commit_work}),
case lists:keysearch(index, 1, Commit) of
false ->
- Index = #index{setorbag = Type,
- pos_list = [IxElem]},
- %% Check later if mnesia_tm is sensative about the order
+ IndexInfo = index_info(SetOrBag, [IxElem]),
+ %% Check later if mnesia_tm is sensitive about the order
+ mnesia_lib:set({Tab, index_info}, IndexInfo),
+ mnesia_lib:set({Tab, index}, index_positions(IndexInfo)),
mnesia_lib:set({Tab, commit_work},
- mnesia_lib:sort_commit([Index | Commit]));
+ mnesia_lib:sort_commit([IndexInfo | Commit]));
{value, Old} ->
%% We could check for consistency here
Index = Old#index{pos_list = [IxElem | Old#index.pos_list]},
+ mnesia_lib:set({Tab, index_info}, Index),
+ mnesia_lib:set({Tab, index}, index_positions(Index)),
NewC = lists:keyreplace(index, 1, Commit, Index),
mnesia_lib:set({Tab, commit_work},
mnesia_lib:sort_commit(NewC))
end.
+index_positions(#index{pos_list = PL}) ->
+ [P || {{P,_},_} <- PL].
+
del_index_info(Tab, Pos) ->
Commit = val({Tab, commit_work}),
case lists:keysearch(index, 1, Commit) of
@@ -345,13 +484,21 @@ del_index_info(Tab, Pos) ->
%% Something is wrong ignore
skip;
{value, Old} ->
- case lists:keydelete(Pos, 1, Old#index.pos_list) of
+ case lists:filter(fun({P,_}) ->
+ element(1,P)=/=Pos
+ end,
+ Old#index.pos_list) of
[] ->
+ IndexInfo = index_info(Old#index.setorbag,[]),
+ mnesia_lib:set({Tab, index_info}, IndexInfo),
+ mnesia_lib:set({Tab, index}, index_positions(IndexInfo)),
NewC = lists:keydelete(index, 1, Commit),
mnesia_lib:set({Tab, commit_work},
mnesia_lib:sort_commit(NewC));
New ->
Index = Old#index{pos_list = New},
+ mnesia_lib:set({Tab, index_info}, Index),
+ mnesia_lib:set({Tab, index}, index_positions(Index)),
NewC = lists:keyreplace(index, 1, Commit, Index),
mnesia_lib:set({Tab, commit_work},
mnesia_lib:sort_commit(NewC))
@@ -360,33 +507,69 @@ del_index_info(Tab, Pos) ->
db_put({ram, Ixt}, V) ->
true = ?ets_insert(Ixt, V);
+db_put({{ext, _, _} = Ext, Ixt}, V) ->
+ mnesia_lib:db_put(Ext, Ixt, V);
db_put({dets, Ixt}, V) ->
ok = dets:insert(Ixt, V).
-db_get({ram, Ixt}, K) ->
- ?ets_lookup(Ixt, K);
+db_get({ram, _}=Ixt, IxKey) ->
+ Pat = [{{{IxKey, '$1'}}, [], [{{IxKey,'$1'}}]}],
+ db_select(Ixt, Pat);
+db_get({{ext,_,_} = _Storage, {_,_,{_,Type}}} = Ixt, IxKey) ->
+ Pat = case Type of
+ ordered -> [{{{IxKey, '$1'}}, [], [{{IxKey,'$1'}}]}];
+ bag -> [{{IxKey, '_'}, [], ['$_']}]
+ end,
+ db_select(Ixt, Pat);
db_get({dets, Ixt}, K) ->
dets:lookup(Ixt, K).
+db_erase({ram, Ixt}, K) ->
+ ?ets_delete(Ixt, K);
+db_erase({{ext,_,_} = Ext, Ixt}, K) ->
+ mnesia_lib:db_erase(Ext, Ixt, K);
+db_erase({dets, Ixt}, K) ->
+ dets:delete(Ixt, K).
+
db_match_erase({ram, Ixt}, Pat) ->
true = ?ets_match_delete(Ixt, Pat);
+db_match_erase({{ext,_,_} = Ext, Ixt}, Pat) ->
+ mnesia_lib:db_match_erase(Ext, Ixt, Pat);
db_match_erase({dets, Ixt}, Pat) ->
ok = dets:match_delete(Ixt, Pat).
-db_match({ram, Ixt}, Pat) ->
- ?ets_match(Ixt, Pat);
-db_match({dets, Ixt}, Pat) ->
- dets:match(Ixt, Pat).
+db_select({ram, Ixt}, Pat) ->
+ ets:select(Ixt, Pat);
+db_select({{ext,_,_} = Ext, Ixt}, Pat) ->
+ mnesia_lib:db_select(Ext, Ixt, Pat);
+db_select({dets, Ixt}, Pat) ->
+ dets:select(Ixt, Pat).
+
get_index_table(Tab, Pos) ->
get_index_table(Tab, val({Tab, storage_type}), Pos).
-get_index_table(Tab, ram_copies, Pos) ->
- {ram, val({Tab, {index, Pos}})};
-get_index_table(Tab, disc_copies, Pos) ->
- {ram, val({Tab, {index, Pos}})};
-get_index_table(Tab, disc_only_copies, Pos) ->
- {dets, val({Tab, {index, Pos}})};
-get_index_table(_Tab, unknown, _Pos) ->
- unknown.
-
+get_index_table(Tab, _Storage, Pos) ->
+ #index{pos_list = PosL} = val({Tab, index_info}),
+ {_IxType, Ixt} = pick_index(PosL, Tab, Pos),
+ Ixt.
+
+index_vals_f(Storage, Tab, {_} = Pos) ->
+ index_vals_f(Storage, Tab,
+ lists:keyfind(Pos, 1, mnesia_schema:index_plugins()));
+index_vals_f(_Storage, Tab, {Pos,M,F}) ->
+ fun(Obj) ->
+ M:F(Tab, Pos, Obj)
+ end;
+index_vals_f(Storage, Tab, Pos) when is_integer(Pos) ->
+ case mnesia_lib:semantics(Storage, index_fun) of
+ undefined ->
+ fun(Obj) ->
+ [element(Pos, Obj)]
+ end;
+ F when is_function(F, 4) ->
+ {ext, Alias, _Mod} = Storage,
+ fun(Obj) ->
+ F(Alias, Tab, Pos, Obj)
+ end
+ end.
diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl
index a5eded4f0f..10e232c800 100644
--- a/lib/mnesia/src/mnesia_lib.erl
+++ b/lib/mnesia/src/mnesia_lib.erl
@@ -54,6 +54,7 @@
db_erase_tab/2,
db_first/1,
db_first/2,
+ db_foldl/3, db_foldl/4, db_foldl/6,
db_last/1,
db_last/2,
db_fixtable/3,
@@ -135,6 +136,7 @@
set_local_content_whereabouts/1,
set_remote_where_to_read/1,
set_remote_where_to_read/2,
+ semantics/2,
show/1,
show/2,
sort_commit/1,
@@ -144,6 +146,7 @@
tab2tmp/1,
tab2dcd/1,
tab2dcl/1,
+ tab2logtmp/1,
to_list/1,
union/2,
uniq/1,
@@ -151,6 +154,8 @@
unset/1,
%% update_counter/2,
val/1,
+ validate_key/2,
+ validate_record/2,
vcore/0,
vcore/1,
verbose/2,
@@ -319,15 +324,41 @@ tab2dcd(Tab) -> %% Disc copies data
tab2dcl(Tab) -> %% Disc copies log
dir(lists:concat([Tab, ".DCL"])).
+tab2logtmp(Tab) -> %% Disc copies log
+ dir(lists:concat([Tab, ".LOGTMP"])).
+
storage_type_at_node(Node, Tab) ->
search_key(Node, [{disc_copies, val({Tab, disc_copies})},
{ram_copies, val({Tab, ram_copies})},
- {disc_only_copies, val({Tab, disc_only_copies})}]).
+ {disc_only_copies, val({Tab, disc_only_copies})}|
+ wrap_external(val({Tab, external_copies}))]).
cs_to_storage_type(Node, Cs) ->
search_key(Node, [{disc_copies, Cs#cstruct.disc_copies},
{ram_copies, Cs#cstruct.ram_copies},
- {disc_only_copies, Cs#cstruct.disc_only_copies}]).
+ {disc_only_copies, Cs#cstruct.disc_only_copies} |
+ wrap_external(Cs#cstruct.external_copies)]).
+
+-define(native(T), T==ram_copies; T==disc_copies; T==disc_only_copies).
+
+semantics({ext,Alias,Mod}, Item) ->
+ Mod:semantics(Alias, Item);
+semantics({Alias,Mod}, Item) ->
+ Mod:semantics(Alias, Item);
+semantics(Type, storage) when ?native(Type) ->
+ Type;
+semantics(Type, types) when ?native(Type) ->
+ [set, ordered_set, bag];
+semantics(disc_only_copies, index_types) ->
+ [bag];
+semantics(Type, index_types) when ?native(Type) ->
+ [bag, ordered];
+semantics(_, _) ->
+ undefined.
+
+
+wrap_external(L) ->
+ [{{ext,Alias,Mod},Ns} || {{Alias,Mod},Ns} <- L].
schema_cs_to_storage_type(Node, Cs) ->
case cs_to_storage_type(Node, Cs) of
@@ -335,7 +366,6 @@ schema_cs_to_storage_type(Node, Cs) ->
Other -> Other
end.
-
search_key(Key, [{Val, List} | Tail]) ->
case lists:member(Key, List) of
true -> Val;
@@ -344,6 +374,33 @@ search_key(Key, [{Val, List} | Tail]) ->
search_key(_Key, []) ->
unknown.
+validate_key(Tab, Key) ->
+ case ?catch_val({Tab, record_validation}) of
+ {RecName, Arity, Type} ->
+ {RecName, Arity, Type};
+ {RecName, Arity, Type, Alias, Mod} ->
+ %% external type
+ Mod:validate_key(Alias, Tab, RecName, Arity, Type, Key);
+ {'EXIT', _} ->
+ mnesia:abort({no_exists, Tab})
+ end.
+
+
+validate_record(Tab, Obj) ->
+ case ?catch_val({Tab, record_validation}) of
+ {RecName, Arity, Type}
+ when tuple_size(Obj) == Arity, RecName == element(1, Obj) ->
+ {RecName, Arity, Type};
+ {RecName, Arity, Type, Alias, Mod}
+ when tuple_size(Obj) == Arity, RecName == element(1, Obj) ->
+ %% external type
+ Mod:validate_record(Alias, Tab, RecName, Arity, Type, Obj);
+ {'EXIT', _} ->
+ mnesia:abort({no_exists, Tab});
+ _ ->
+ mnesia:abort({bad_type, Obj})
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ops, we've got some global variables here :-)
@@ -410,9 +467,9 @@ pr_other(Var) ->
no -> {node_not_running, node()};
_ -> {no_exists, Var}
end,
- verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~n",
+ verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~p ~n",
[self(), process_info(self(), registered_name),
- Var, Why]),
+ Var, Why, erlang:get_stacktrace()]),
mnesia:abort(Why).
%% Some functions for list valued variables
@@ -549,10 +606,16 @@ read_counter(Name) ->
?ets_lookup_element(mnesia_stats, Name, 2).
cs_to_nodes(Cs) ->
+ ext_nodes(Cs#cstruct.external_copies) ++
Cs#cstruct.disc_only_copies ++
Cs#cstruct.disc_copies ++
Cs#cstruct.ram_copies.
+ext_nodes(Ext) ->
+ lists:flatmap(fun({_, Ns}) ->
+ Ns
+ end, Ext).
+
overload_types() ->
[mnesia_tm, mnesia_dump_log].
@@ -748,7 +811,7 @@ view(File) ->
true ->
view(File, dat);
false ->
- case suffix([".LOG", ".BUP", ".ETS"], File) of
+ case suffix([".LOG", ".BUP", ".ETS", ".LOGTMP"], File) of
true ->
view(File, log);
false ->
@@ -1044,18 +1107,24 @@ db_get(Tab, Key) ->
db_get(val({Tab, storage_type}), Tab, Key).
db_get(ram_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
db_get(disc_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
-db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key).
+db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key);
+db_get({ext, Alias, Mod}, Tab, Key) ->
+ Mod:lookup(Alias, Tab, Key).
db_init_chunk(Tab) ->
db_init_chunk(val({Tab, storage_type}), Tab, 1000).
db_init_chunk(Tab, N) ->
db_init_chunk(val({Tab, storage_type}), Tab, N).
+db_init_chunk({ext, Alias, Mod}, Tab, N) ->
+ Mod:select(Alias, Tab, [{'_', [], ['$_']}], N);
db_init_chunk(disc_only_copies, Tab, N) ->
dets:select(Tab, [{'_', [], ['$_']}], N);
db_init_chunk(_, Tab, N) ->
ets:select(Tab, [{'_', [], ['$_']}], N).
+db_chunk({ext, _Alias, Mod}, State) ->
+ Mod:select(State);
db_chunk(disc_only_copies, State) ->
dets:select(State);
db_chunk(_, State) ->
@@ -1066,7 +1135,9 @@ db_put(Tab, Val) ->
db_put(ram_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
db_put(disc_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
-db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val).
+db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val);
+db_put({ext, Alias, Mod}, Tab, Val) ->
+ Mod:insert(Alias, Tab, Val).
db_match_object(Tab, Pat) ->
db_match_object(val({Tab, storage_type}), Tab, Pat).
@@ -1075,12 +1146,36 @@ db_match_object(Storage, Tab, Pat) ->
try
case Storage of
disc_only_copies -> dets:match_object(Tab, Pat);
+ {ext, Alias, Mod} -> Mod:select(Alias, Tab, [{Pat, [], ['$_']}]);
_ -> ets:match_object(Tab, Pat)
end
after
db_fixtable(Storage, Tab, false)
end.
+db_foldl(Fun, Acc, Tab) ->
+ db_foldl(val({Tab, storage_type}), Fun, Acc, Tab).
+
+db_foldl(Storage, Fun, Acc, Tab) ->
+ Limit = mnesia_monitor:get_env(fold_chunk_size),
+ db_foldl(Storage, Fun, Acc, Tab, [{'_', [], ['$_']}], Limit).
+
+db_foldl(ram_copies, Fun, Acc, Tab, Pat, Limit) ->
+ mnesia_lib:db_fixtable(ram_copies, Tab, true),
+ try select_foldl(db_select_init(ram_copies, Tab, Pat, Limit),
+ Fun, Acc, ram_copies)
+ after
+ mnesia_lib:db_fixtable(ram_copies, Tab, false)
+ end;
+db_foldl(Storage, Fun, Acc, Tab, Pat, Limit) ->
+ select_foldl(mnesia_lib:db_select_init(Storage, Tab, Pat, Limit), Fun, Acc, Storage).
+
+select_foldl({Objs, Cont}, Fun, Acc, Storage) ->
+ select_foldl(mnesia_lib:db_select_cont(Storage, Cont, []),
+ Fun, lists:foldl(Fun, Acc, Objs), Storage);
+select_foldl('$end_of_table', _, Acc, _) ->
+ Acc.
+
db_select(Tab, Pat) ->
db_select(val({Tab, storage_type}), Tab, Pat).
@@ -1089,17 +1184,33 @@ db_select(Storage, Tab, Pat) ->
try
case Storage of
disc_only_copies -> dets:select(Tab, Pat);
+ {ext, Alias, Mod} -> Mod:select(Alias, Tab, Pat);
_ -> ets:select(Tab, Pat)
end
after
db_fixtable(Storage, Tab, false)
end.
+db_select_init({ext, Alias, Mod}, Tab, Pat, Limit) ->
+ case Mod:select(Alias, Tab, Pat, Limit) of
+ {Matches, Continuation} when is_list(Matches) ->
+ {Matches, {Alias, Continuation}};
+ R ->
+ R
+ end;
db_select_init(disc_only_copies, Tab, Pat, Limit) ->
dets:select(Tab, Pat, Limit);
db_select_init(_, Tab, Pat, Limit) ->
ets:select(Tab, Pat, Limit).
+db_select_cont({ext, Alias, Mod}, Cont0, Ms) ->
+ Cont = Mod:repair_continuation(Cont0, Ms),
+ case Mod:select(Cont) of
+ {Matches, Continuation} when is_list(Matches) ->
+ {Matches, {Alias, Continuation}};
+ R ->
+ R
+ end;
db_select_cont(disc_only_copies, Cont0, Ms) ->
Cont = dets:repair_continuation(Cont0, Ms),
dets:select(Cont);
@@ -1116,13 +1227,18 @@ db_fixtable(disc_copies, Tab, Bool) ->
db_fixtable(dets, Tab, Bool) ->
dets:safe_fixtable(Tab, Bool);
db_fixtable(disc_only_copies, Tab, Bool) ->
- dets:safe_fixtable(Tab, Bool).
+ dets:safe_fixtable(Tab, Bool);
+db_fixtable({ext, Alias, Mod}, Tab, Bool) ->
+ Mod:fixtable(Alias, Tab, Bool).
db_erase(Tab, Key) ->
db_erase(val({Tab, storage_type}), Tab, Key).
db_erase(ram_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
db_erase(disc_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
-db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key).
+db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key);
+db_erase({ext, Alias, Mod}, Tab, Key) ->
+ Mod:delete(Alias, Tab, Key),
+ ok.
db_match_erase(Tab, '_') ->
db_delete_all(val({Tab, storage_type}),Tab);
@@ -1130,7 +1246,10 @@ db_match_erase(Tab, Pat) ->
db_match_erase(val({Tab, storage_type}), Tab, Pat).
db_match_erase(ram_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
db_match_erase(disc_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
-db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat).
+db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat);
+db_match_erase({ext, Alias, Mod}, Tab, Pat) ->
+ Mod:match_delete(Alias, Tab, Pat),
+ ok.
db_delete_all(ram_copies, Tab) -> ets:delete_all_objects(Tab);
db_delete_all(disc_copies, Tab) -> ets:delete_all_objects(Tab);
@@ -1140,31 +1259,41 @@ db_first(Tab) ->
db_first(val({Tab, storage_type}), Tab).
db_first(ram_copies, Tab) -> ?ets_first(Tab);
db_first(disc_copies, Tab) -> ?ets_first(Tab);
-db_first(disc_only_copies, Tab) -> dets:first(Tab).
+db_first(disc_only_copies, Tab) -> dets:first(Tab);
+db_first({ext, Alias, Mod}, Tab) ->
+ Mod:first(Alias, Tab).
db_next_key(Tab, Key) ->
db_next_key(val({Tab, storage_type}), Tab, Key).
db_next_key(ram_copies, Tab, Key) -> ?ets_next(Tab, Key);
db_next_key(disc_copies, Tab, Key) -> ?ets_next(Tab, Key);
-db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key).
+db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key);
+db_next_key({ext, Alias, Mod}, Tab, Key) ->
+ Mod:next(Alias, Tab, Key).
db_last(Tab) ->
db_last(val({Tab, storage_type}), Tab).
db_last(ram_copies, Tab) -> ?ets_last(Tab);
db_last(disc_copies, Tab) -> ?ets_last(Tab);
-db_last(disc_only_copies, Tab) -> dets:first(Tab). %% Dets don't have order
+db_last(disc_only_copies, Tab) -> dets:first(Tab); %% Dets don't have order
+db_last({ext, Alias, Mod}, Tab) ->
+ Mod:last(Alias, Tab).
db_prev_key(Tab, Key) ->
db_prev_key(val({Tab, storage_type}), Tab, Key).
db_prev_key(ram_copies, Tab, Key) -> ?ets_prev(Tab, Key);
db_prev_key(disc_copies, Tab, Key) -> ?ets_prev(Tab, Key);
-db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key). %% Dets don't have order
+db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key); %% Dets don't have order
+db_prev_key({ext, Alias, Mod}, Tab, Key) ->
+ Mod:prev(Alias, Tab, Key).
db_slot(Tab, Pos) ->
db_slot(val({Tab, storage_type}), Tab, Pos).
db_slot(ram_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
db_slot(disc_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
-db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos).
+db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos);
+db_slot({ext, Alias, Mod}, Tab, Pos) ->
+ Mod:slot(Alias, Tab, Pos).
db_update_counter(Tab, C, Val) ->
db_update_counter(val({Tab, storage_type}), Tab, C, Val).
@@ -1173,13 +1302,16 @@ db_update_counter(ram_copies, Tab, C, Val) ->
db_update_counter(disc_copies, Tab, C, Val) ->
?ets_update_counter(Tab, C, Val);
db_update_counter(disc_only_copies, Tab, C, Val) ->
- dets:update_counter(Tab, C, Val).
+ dets:update_counter(Tab, C, Val);
+db_update_counter({ext, Alias, Mod}, Tab, C, Val) ->
+ Mod:update_counter(Alias, Tab, C, Val).
db_erase_tab(Tab) ->
db_erase_tab(val({Tab, storage_type}), Tab).
db_erase_tab(ram_copies, Tab) -> ?ets_delete_table(Tab);
db_erase_tab(disc_copies, Tab) -> ?ets_delete_table(Tab);
-db_erase_tab(disc_only_copies, _Tab) -> ignore.
+db_erase_tab(disc_only_copies, _Tab) -> ignore;
+db_erase_tab({ext, _Alias, _Mod}, _Tab) -> ignore.
%% assuming that Tab is a valid ets-table
dets_to_ets(Tabname, Tab, File, Type, Rep, Lock) ->
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index bde5585139..71e5829c87 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -147,6 +147,19 @@ do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_only_copies -
{error, Error} ->
{not_loaded, {"Failed to create dets table", Error}}
end
+ end;
+
+do_get_disc_copy2(Tab, Reason, Storage = {ext, Alias, Mod}, _Type) ->
+ Cs = val({Tab, cstruct}),
+ case mnesia_monitor:unsafe_create_external(Tab, Alias, Mod, Cs) of
+ ok ->
+ ok = ext_load_table(Mod, Alias, Tab, Reason),
+ mnesia_index:init_index(Tab, Storage),
+ set({Tab, load_node}, node()),
+ set({Tab, load_reason}, Reason),
+ {loaded, ok};
+ Other ->
+ {not_loaded, Other}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -259,7 +272,7 @@ init_receiver(Node, Tab,Storage,Cs,Reason) ->
true = lists:member(Node, Active),
{SenderPid, TabSize, DetsData} =
start_remote_sender(Node,Tab,Storage),
- Init = table_init_fun(SenderPid),
+ Init = table_init_fun(SenderPid, Storage),
Args = [self(),Tab,Storage,Cs,SenderPid,
TabSize,DetsData,Init],
Pid = spawn_link(?MODULE, spawned_receiver, Args),
@@ -289,7 +302,12 @@ start_remote_sender(Node,Tab,Storage) ->
mnesia_controller:start_remote_sender(Node, Tab, self(), Storage),
put(mnesia_table_sender_node, {Tab, Node}),
receive
- {SenderPid, {first, TabSize}} ->
+ {SenderPid, {first, _} = Msg}
+ when is_pid(SenderPid), element(1, Storage) == ext ->
+ {ext, Alias, Mod} = Storage,
+ {Sz, Data} = Mod:receiver_first_message(SenderPid, Msg, Alias, Tab),
+ {SenderPid, Sz, Data};
+ {SenderPid, {first, TabSize}} =_M1 ->
{SenderPid, TabSize, false};
{SenderPid, {first, TabSize, DetsData}} ->
{SenderPid, TabSize, DetsData};
@@ -299,16 +317,18 @@ start_remote_sender(Node,Tab,Storage) ->
down(Tab, Storage)
end.
-table_init_fun(SenderPid) ->
+table_init_fun(SenderPid, Storage) ->
fun(read) ->
Receiver = self(),
SenderPid ! {Receiver, more},
- get_data(SenderPid, Receiver)
+ get_data(SenderPid, Receiver, Storage);
+ (close) ->
+ ok
end.
%% Add_table_copy get's it's own locks.
start_receiver(Tab,Storage,Cs,SenderPid,TabSize,DetsData,{dumper,{add_table_copy,_}}) ->
- Init = table_init_fun(SenderPid),
+ Init = table_init_fun(SenderPid, Storage),
case do_init_table(Tab,Storage,Cs,SenderPid,TabSize,DetsData,self(), Init) of
Err = {error, _} ->
SenderPid ! {copier_done, node()},
@@ -391,7 +411,15 @@ create_table(Tab, TabSize, Storage, Cs) ->
{Storage, Tab};
Else ->
Else
- end
+ end;
+ element(1, Storage) == ext ->
+ {_, Alias, Mod} = Storage,
+ case mnesia_monitor:unsafe_create_external(Tab, Alias, Mod, Cs) of
+ ok ->
+ {Storage, Tab};
+ Else ->
+ Else
+ end
end.
tab_receiver(Node, Tab, Storage, Cs, OrigTabRec) ->
@@ -409,21 +437,25 @@ tab_receiver(Node, Tab, Storage, Cs, OrigTabRec) ->
tab_receiver(Node, Tab, Storage, Cs, OrigTabRec)
end.
-make_table_fun(Pid, TabRec) ->
+make_table_fun(Pid, TabRec, Storage) ->
fun(close) ->
ok;
+ ({read, Msg}) ->
+ Pid ! {TabRec, Msg},
+ get_data(Pid, TabRec, Storage);
(read) ->
- get_data(Pid, TabRec)
+ get_data(Pid, TabRec, Storage)
end.
-get_data(Pid, TabRec) ->
+get_data(Pid, TabRec, Storage) ->
receive
{Pid, {more_z, CompressedRecs}} when is_binary(CompressedRecs) ->
- Pid ! {TabRec, more},
- {zlib_uncompress(CompressedRecs), make_table_fun(Pid,TabRec)};
+ maybe_reply(Pid, {TabRec, more}, Storage),
+ {zlib_uncompress(CompressedRecs),
+ make_table_fun(Pid, TabRec, Storage)};
{Pid, {more, Recs}} ->
- Pid ! {TabRec, more},
- {Recs, make_table_fun(Pid,TabRec)};
+ maybe_reply(Pid, {TabRec, more}, Storage),
+ {Recs, make_table_fun(Pid, TabRec, Storage)};
{Pid, no_more} ->
end_of_input;
{copier_done, Node} ->
@@ -431,13 +463,48 @@ get_data(Pid, TabRec) ->
Node ->
{copier_done, Node};
_ ->
- get_data(Pid, TabRec)
+ get_data(Pid, TabRec, Storage)
end;
{'EXIT', Pid, Reason} ->
handle_exit(Pid, Reason),
- get_data(Pid, TabRec)
+ get_data(Pid, TabRec, Storage)
end.
+maybe_reply(_, _, {ext, _, _}) ->
+ ignore;
+maybe_reply(Pid, Msg, _) ->
+ Pid ! Msg.
+
+ext_init_table(Alias, Mod, Tab, Fun, State, Sender) ->
+ ok = ext_load_table(Mod, Alias, Tab, {net_load, node(Sender)}),
+ ext_init_table(read, Alias, Mod, Tab, Fun, State, Sender).
+
+ext_load_table(Mod, Alias, Tab, Reason) ->
+ CS = val({Tab, cstruct}),
+ Mod:load_table(Alias, Tab, Reason, mnesia_schema:cs2list(CS)).
+
+
+ext_init_table(Action, Alias, Mod, Tab, Fun, State, Sender) ->
+ case Fun(Action) of
+ {copier_done, Node} ->
+ verbose("Receiver of table ~p crashed on ~p (more)~n", [Tab, Node]),
+ down(Tab, {ext,Alias,Mod});
+ {Data, NewFun} ->
+ case Mod:receive_data(Data, Alias, Tab, Sender, State) of
+ {more, NewState} ->
+ ext_init_table({read, more}, Alias, Mod,
+ Tab, NewFun, NewState, Sender);
+ {{more,Msg}, NewState} ->
+ ext_init_table({read, Msg}, Alias, Mod,
+ Tab, NewFun, NewState, Sender)
+ end;
+ end_of_input ->
+ Mod:receive_done(Alias, Tab, Sender, State),
+ ok = Fun(close)
+ end.
+
+init_table(Tab, {ext,Alias,Mod}, Fun, State, Sender) ->
+ ext_init_table(Alias, Mod, Tab, Fun, State, Sender);
init_table(Tab, disc_only_copies, Fun, DetsInfo,Sender) ->
ErtsVer = erlang:system_info(version),
case DetsInfo of
@@ -567,7 +634,10 @@ handle_last({ram_copies, Tab}, _Type, DatBin) ->
ok;
false ->
ok
- end.
+ end;
+
+handle_last(_Storage, _Type, nobin) ->
+ ok.
down(Tab, Storage) ->
case Storage of
@@ -578,7 +648,10 @@ down(Tab, Storage) ->
disc_only_copies ->
TmpFile = mnesia_lib:tab2tmp(Tab),
mnesia_lib:dets_sync_close(Tab),
- file:delete(TmpFile)
+ file:delete(TmpFile);
+ {ext, Alias, Mod} ->
+ catch Mod:close_table(Alias, Tab),
+ catch Mod:delete_table(Alias, Tab)
end,
mnesia_checkpoint:tm_del_copy(Tab, node()),
mnesia_controller:sync_del_table_copy_whereabouts(Tab, node()),
@@ -603,21 +676,35 @@ db_erase({ram_copies, Tab}, Key) ->
db_erase({disc_copies, Tab}, Key) ->
true = ?ets_delete(Tab, Key);
db_erase({disc_only_copies, Tab}, Key) ->
- ok = dets:delete(Tab, Key).
+ ok = dets:delete(Tab, Key);
+db_erase({{ext, Alias, Mod}, Tab}, Key) ->
+ ok = Mod:delete(Alias, Tab, Key).
db_match_erase({ram_copies, Tab} , Pat) ->
true = ?ets_match_delete(Tab, Pat);
db_match_erase({disc_copies, Tab} , Pat) ->
true = ?ets_match_delete(Tab, Pat);
db_match_erase({disc_only_copies, Tab}, Pat) ->
- ok = dets:match_delete(Tab, Pat).
+ ok = dets:match_delete(Tab, Pat);
+db_match_erase({{ext, Alias, Mod}, Tab}, Pat) ->
+ % "ets style" is to return true
+ % "dets style" is to return N | { error, Reason }
+ % or sometimes ok (?)
+ % be nice and accept both
+ case Mod:match_delete(Alias, Tab, Pat) of
+ N when is_integer (N) -> ok;
+ true -> ok;
+ ok -> ok
+ end.
db_put({ram_copies, Tab}, Val) ->
true = ?ets_insert(Tab, Val);
db_put({disc_copies, Tab}, Val) ->
true = ?ets_insert(Tab, Val);
db_put({disc_only_copies, Tab}, Val) ->
- ok = dets:insert(Tab, Val).
+ ok = dets:insert(Tab, Val);
+db_put({{ext, Alias, Mod}, Tab}, Val) ->
+ ok = Mod:insert(Alias, Tab, Val).
%% This code executes at the remote site where the data is
%% executes in a special copier process.
@@ -636,47 +723,62 @@ send_table(Pid, Tab, RemoteS) ->
unknown ->
{error, {no_exists, Tab}};
Storage ->
- %% Send first
- TabSize = mnesia:table_info(Tab, size),
- KeysPerTransfer = calc_nokeys(Storage, Tab),
- ChunkData = dets:info(Tab, bchunk_format),
-
- UseDetsChunk =
- Storage == RemoteS andalso
- Storage == disc_only_copies andalso
- ChunkData /= undefined,
- if
- UseDetsChunk == true ->
- DetsInfo = erlang:system_info(version),
- Pid ! {self(), {first, TabSize, {DetsInfo, ChunkData}}};
- true ->
- Pid ! {self(), {first, TabSize}}
- end,
+ do_send_table(Pid, Tab, Storage, RemoteS)
+ end.
- %% Debug info
- put(mnesia_table_sender, {Tab, node(Pid), Pid}),
- {Init, Chunk} = reader_funcs(UseDetsChunk, Tab, Storage, KeysPerTransfer),
-
- SendIt = fun() ->
- NeedLock = need_lock(Tab),
- {atomic, ok} = prepare_copy(Pid, Tab, Storage, NeedLock),
- send_more(Pid, 1, Chunk, Init(), Tab),
- finish_copy(Pid, Tab, Storage, RemoteS, NeedLock)
- end,
-
- try SendIt() of
- {_, receiver_died} -> ok;
- {atomic, no_more} -> ok
- catch
- throw:receiver_died ->
- cleanup_tab_copier(Pid, Storage, Tab),
- ok;
- error:Reason -> %% Prepare failed
- cleanup_tab_copier(Pid, Storage, Tab),
- {error, {tab_copier, Tab, {Reason, erlang:get_stacktrace()}}}
- after
- unlink(whereis(mnesia_tm))
- end
+do_send_table(Pid, Tab, Storage, RemoteS) ->
+ {Init, Chunk} =
+ case Storage of
+ {ext, Alias, Mod} ->
+ case Mod:sender_init(Alias, Tab, RemoteS, Pid) of
+ {standard, I, C} ->
+ Pid ! {self(), {first, Mod:info(Alias, Tab, size)}},
+ {I, C};
+ {_, _} = Res ->
+ Res
+ end;
+ Storage ->
+ %% Send first
+ TabSize = mnesia:table_info(Tab, size),
+ KeysPerTransfer = calc_nokeys(Storage, Tab),
+ ChunkData = dets:info(Tab, bchunk_format),
+
+ UseDetsChunk =
+ Storage == RemoteS andalso
+ Storage == disc_only_copies andalso
+ ChunkData /= undefined,
+ if
+ UseDetsChunk == true ->
+ DetsInfo = erlang:system_info(version),
+ Pid ! {self(), {first, TabSize, {DetsInfo, ChunkData}}};
+ true ->
+ Pid ! {self(), {first, TabSize}}
+ end,
+ {_I, _C} =
+ reader_funcs(UseDetsChunk, Tab, Storage, KeysPerTransfer)
+ end,
+ %% Debug info
+ put(mnesia_table_sender, {Tab, node(Pid), Pid}),
+
+ SendIt = fun() ->
+ NeedLock = need_lock(Tab),
+ {atomic, ok} = prepare_copy(Pid, Tab, Storage, NeedLock),
+ send_more(Pid, 1, Chunk, Init(), Tab, Storage),
+ finish_copy(Pid, Tab, Storage, RemoteS, NeedLock)
+ end,
+
+ try SendIt() of
+ {_, receiver_died} -> ok;
+ {atomic, no_more} -> ok
+ catch
+ throw:receiver_died ->
+ cleanup_tab_copier(Pid, Storage, Tab),
+ ok;
+ error:Reason -> %% Prepare failed
+ cleanup_tab_copier(Pid, Storage, Tab),
+ {error, {tab_copier, Tab, {Reason, erlang:get_stacktrace()}}}
+ after
+ unlink(whereis(mnesia_tm))
end.
prepare_copy(Pid, Tab, Storage, NeedLock) ->
@@ -726,20 +828,32 @@ update_where_to_write([H|T], Tab, AddNode) ->
[{update_where_to_write, [add, Tab, AddNode], self()}]),
update_where_to_write(T, Tab, AddNode).
-send_more(Pid, N, Chunk, DataState, Tab) ->
+send_more(Pid, N, Chunk, DataState, Tab, Storage) ->
receive
{NewPid, more} ->
case send_packet(N - 1, NewPid, Chunk, DataState) of
New when is_integer(New) ->
New - 1;
NewData ->
- send_more(NewPid, ?MAX_NOPACKETS, Chunk, NewData, Tab)
+ send_more(NewPid, ?MAX_NOPACKETS, Chunk, NewData,
+ Tab, Storage)
+ end;
+ {NewPid, {more, Msg}} when element(1, Storage) == ext ->
+ {ext, Alias, Mod} = Storage,
+ {NewChunk, NewState} =
+ Mod:sender_handle_info(Msg, Alias, Tab, NewPid, DataState),
+ case send_packet(N - 1, NewPid, NewChunk, NewState) of
+ New when is_integer(New) ->
+ New -1;
+ NewData ->
+ send_more(NewPid, N, NewChunk, NewData, Tab,
+ Storage)
end;
{_NewPid, {old_protocol, Tab}} ->
Storage = val({Tab, storage_type}),
{Init, NewChunk} =
reader_funcs(false, Tab, Storage, calc_nokeys(Storage, Tab)),
- send_more(Pid, 1, NewChunk, Init(), Tab);
+ send_more(Pid, 1, NewChunk, Init(), Tab, Storage);
{copier_done, Node} when Node == node(Pid)->
verbose("Receiver of table ~p crashed on ~p (more)~n", [Tab, Node]),
diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl
index 36135418c8..9536effd42 100644
--- a/lib/mnesia/src/mnesia_log.erl
+++ b/lib/mnesia/src/mnesia_log.erl
@@ -224,17 +224,12 @@ sappend(Log, Term) ->
ok = disk_log:log(Log, Term).
%% Write commit records to the latest_log
-log(C) when C#commit.disc_copies == [],
- C#commit.disc_only_copies == [],
- C#commit.schema_ops == [] ->
- ignore;
log(C) ->
- case mnesia_monitor:use_dir() of
+ case need_log(C) andalso mnesia_monitor:use_dir() of
true ->
if
is_record(C, commit) ->
- C2 = C#commit{ram_copies = [], snmp = []},
- append(latest_log, C2);
+ append(latest_log, strip_snmp(C));
true ->
%% Either a commit record as binary
%% or some decision related info
@@ -247,17 +242,12 @@ log(C) ->
%% Synced
-slog(C) when C#commit.disc_copies == [],
- C#commit.disc_only_copies == [],
- C#commit.schema_ops == [] ->
- ignore;
slog(C) ->
- case mnesia_monitor:use_dir() of
+ case need_log(C) andalso mnesia_monitor:use_dir() of
true ->
if
is_record(C, commit) ->
- C2 = C#commit{ram_copies = [], snmp = []},
- sappend(latest_log, C2);
+ sappend(latest_log, strip_snmp(C));
true ->
%% Either a commit record as binary
%% or some decision related info
@@ -268,6 +258,13 @@ slog(C) ->
ignore
end.
+need_log(#commit{disc_copies=[], disc_only_copies=[], schema_ops=[], ext=Ext}) ->
+ lists:keymember(ext_copies, 1, Ext);
+need_log(_) -> true.
+
+strip_snmp(#commit{ext=[]}=CR) -> CR;
+strip_snmp(#commit{ext=Ext}=CR) ->
+ CR#commit{ext=lists:keydelete(snmp, 1, Ext)}.
%% Stuff related to the file LOG
diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl
index 4069341700..ab78c9b13e 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.erl
@@ -32,6 +32,7 @@
init/0,
mktab/2,
unsafe_mktab/2,
+ unsafe_create_external/4,
mnesia_down/2,
needs_protocol_conversion/1,
negotiate_protocol/1,
@@ -82,9 +83,9 @@
going_down = [], tm_started = false, early_connects = [],
connecting, mq = [], remote_node_status = []}).
--define(current_protocol_version, {8,2}).
+-define(current_protocol_version, {8,3}).
--define(previous_protocol_version, {8,1}).
+-define(previous_protocol_version, {8,2}).
start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE,
@@ -129,6 +130,8 @@ close_log(Name) ->
unsafe_close_log(Name) ->
unsafe_call({unsafe_close_log, Name}).
+unsafe_create_external(Tab, Alias, Mod, Cs) ->
+ unsafe_call({unsafe_create_external, Tab, Alias, Mod, Cs}).
disconnect(Node) ->
cast({disconnect, Node}).
@@ -193,7 +196,7 @@ protocol_version() ->
%% A sorted list of acceptable protocols the
%% preferred protocols are first in the list
acceptable_protocol_versions() ->
- [protocol_version(), ?previous_protocol_version].
+ [protocol_version(), ?previous_protocol_version, {8,1}].
needs_protocol_conversion(Node) ->
case {?catch_val({protocol, Node}), protocol_version()} of
@@ -405,6 +408,14 @@ handle_call({unsafe_close_log, Name}, _From, State) ->
_ = disk_log:close(Name),
{reply, ok, State};
+handle_call({unsafe_create_external, Tab, Alias, Mod, Cs}, _From, State) ->
+ case catch Mod:create_table(Alias, Tab, mnesia_schema:cs2list(Cs)) of
+ {'EXIT', ExitReason} ->
+ {reply, {error, ExitReason}, State};
+ Reply ->
+ {reply, Reply, State}
+ end;
+
handle_call({negotiate_protocol, Mon, _Version, _Protocols}, _From, State)
when State#state.tm_started == false ->
State2 = State#state{early_connects = [node(Mon) | State#state.early_connects]},
@@ -658,6 +669,7 @@ get_env(E) ->
env() ->
[
access_module,
+ allow_index_on_key,
auto_repair,
backup_module,
debug,
@@ -671,19 +683,23 @@ env() ->
extra_db_nodes,
ignore_fallback_at_startup,
fallback_error_function,
+ fold_chunk_size,
max_wait_for_decision,
schema_location,
core_dir,
pid_sort_order,
no_table_loaders,
dc_dump_limit,
- send_compressed
+ send_compressed,
+ schema
].
default_env(access_module) ->
mnesia;
default_env(auto_repair) ->
true;
+default_env(allow_index_on_key) ->
+ false;
default_env(backup_module) ->
mnesia_backup;
default_env(debug) ->
@@ -709,6 +725,8 @@ default_env(ignore_fallback_at_startup) ->
false;
default_env(fallback_error_function) ->
{mnesia, lkill};
+default_env(fold_chunk_size) ->
+ 100;
default_env(max_wait_for_decision) ->
infinity;
default_env(schema_location) ->
@@ -722,7 +740,9 @@ default_env(no_table_loaders) ->
default_env(dc_dump_limit) ->
4;
default_env(send_compressed) ->
- 0.
+ 0;
+default_env(schema) ->
+ [].
check_type(Env, Val) ->
try do_check_type(Env, Val)
@@ -730,6 +750,7 @@ check_type(Env, Val) ->
end.
do_check_type(access_module, A) when is_atom(A) -> A;
+do_check_type(allow_index_on_key, B) -> bool(B);
do_check_type(auto_repair, B) -> bool(B);
do_check_type(backup_module, B) when is_atom(B) -> B;
do_check_type(debug, debug) -> debug;
@@ -753,6 +774,8 @@ do_check_type(extra_db_nodes, L) when is_list(L) ->
(A) when is_atom(A) -> true
end,
lists:filter(Fun, L);
+do_check_type(fold_chunk_size, I) when is_integer(I), I > 0;
+ I =:= infinity -> I;
do_check_type(max_wait_for_decision, infinity) -> infinity;
do_check_type(max_wait_for_decision, I) when is_integer(I), I > 0 -> I;
do_check_type(schema_location, M) -> media(M);
@@ -766,7 +789,8 @@ do_check_type(pid_sort_order, "standard") -> standard;
do_check_type(pid_sort_order, _) -> false;
do_check_type(no_table_loaders, N) when is_integer(N), N > 0 -> N;
do_check_type(dc_dump_limit,N) when is_number(N), N > 0 -> N;
-do_check_type(send_compressed, L) when is_integer(L), L >= 0, L =< 9 -> L.
+do_check_type(send_compressed, L) when is_integer(L), L >= 0, L =< 9 -> L;
+do_check_type(schema, L) when is_list(L) -> L.
bool(true) -> true;
bool(false) -> false.
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index 782493fb4f..0e4017e4c3 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -29,6 +29,16 @@
-module(mnesia_schema).
-export([
+ add_backend_type/2,
+ do_add_backend_type/2,
+ delete_backend_type/1,
+ do_delete_backend_type/1,
+ backend_types/0,
+ add_index_plugin/3,
+ do_add_index_plugin/3,
+ delete_index_plugin/1,
+ do_delete_index_plugin/1,
+ index_plugins/0,
add_snmp/2,
add_table_copy/3,
add_table_index/2,
@@ -55,14 +65,16 @@
dump_tables/1,
ensure_no_schema/1,
get_create_list/1,
- get_initial_schema/2,
+ get_initial_schema/3,
get_table_properties/1,
info/0,
info/1,
init/1,
+ init_backends/0,
insert_cstruct/3,
is_remote_member/1,
list2cs/1,
+ list2cs/2,
lock_schema/0,
merge_schema/0,
merge_schema/1,
@@ -110,7 +122,8 @@
do_delete_table/1,
do_read_table_property/2,
do_delete_table_property/2,
- do_write_table_property/2]).
+ do_write_table_property/2,
+ do_change_table_copy_type/3]).
-include("mnesia.hrl").
-include_lib("kernel/include/file.hrl").
@@ -138,12 +151,30 @@ init(IgnoreFallback) ->
verbose("Schema initiated from: ~p~n", [Source]),
set({schema, tables}, []),
set({schema, local_tables}, []),
+ do_set_schema(schema),
Tabs = set_schema(?ets_first(schema)),
lists:foreach(fun(Tab) -> clear_whereabouts(Tab) end, Tabs),
set({schema, where_to_read}, node()),
set({schema, load_node}, node()),
set({schema, load_reason}, initial),
- mnesia_controller:add_active_replica(schema, node()).
+ mnesia_controller:add_active_replica(schema, node()),
+ init_backends().
+
+
+init_backends() ->
+ Backends = lists:foldl(fun({Alias, Mod}, Acc) ->
+ orddict:append(Mod, Alias, Acc)
+ end, orddict:new(), get_ext_types()),
+ [init_backend(Mod, Aliases) || {Mod, Aliases} <- Backends],
+ ok.
+
+init_backend(Mod, [_|_] = Aliases) ->
+ case Mod:init_backend() of
+ ok ->
+ Mod:add_aliases(Aliases);
+ Error ->
+ mnesia:abort({backend_init_error, Error})
+ end.
exit_on_error({error, Reason}) ->
exit(Reason);
@@ -180,6 +211,7 @@ do_set_schema(Tab, Cs) ->
set({Tab, ram_copies}, Cs#cstruct.ram_copies),
set({Tab, disc_copies}, Cs#cstruct.disc_copies),
set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies),
+ set({Tab, external_copies}, Cs#cstruct.external_copies),
set({Tab, load_order}, Cs#cstruct.load_order),
set({Tab, access_mode}, Cs#cstruct.access_mode),
set({Tab, majority}, Cs#cstruct.majority),
@@ -195,15 +227,21 @@ do_set_schema(Tab, Cs) ->
set({Tab, arity}, Arity),
RecName = Cs#cstruct.record_name,
set({Tab, record_name}, RecName),
- set({Tab, record_validation}, {RecName, Arity, Type}),
set({Tab, wild_pattern}, wild(RecName, Arity)),
- set({Tab, index}, Cs#cstruct.index),
+ set({Tab, index}, [P || {P,_} <- Cs#cstruct.index]),
+ case Cs#cstruct.index of
+ [] ->
+ set({Tab, index_info}, mnesia_index:index_info(Type, []));
+ _ ->
+ ignore
+ end,
%% create actual index tabs later
set({Tab, cookie}, Cs#cstruct.cookie),
set({Tab, version}, Cs#cstruct.version),
set({Tab, cstruct}, Cs),
Storage = mnesia_lib:schema_cs_to_storage_type(node(), Cs),
set({Tab, storage_type}, Storage),
+ set_record_validation(Tab, Storage, RecName, Arity, Type),
mnesia_lib:add({schema, tables}, Tab),
Ns = mnesia_lib:cs_to_nodes(Cs),
case lists:member(node(), Ns) of
@@ -213,7 +251,24 @@ do_set_schema(Tab, Cs) ->
mnesia_lib:add({schema, local_tables}, Tab);
false ->
ignore
- end.
+ end,
+ set_ext_types(Tab, get_ext_types(), Cs#cstruct.external_copies).
+
+set_record_validation(Tab, {ext,Alias,Mod}, RecName, Arity, Type) ->
+ set({Tab, record_validation}, {RecName, Arity, Type, Alias, Mod});
+set_record_validation(Tab, _, RecName, Arity, Type) ->
+ set({Tab, record_validation}, {RecName, Arity, Type}).
+
+set_ext_types(Tab, ExtTypes, ExtCopies) ->
+ lists:foreach(
+ fun({Type, _} = Key) ->
+ Nodes = case lists:keyfind(Key, 1, ExtCopies) of
+ {_, Ns} -> Ns;
+ false -> []
+ end,
+ set({Tab, Type}, Nodes)
+ end, ExtTypes).
+
wild(RecName, Arity) ->
Wp0 = list_to_tuple(lists:duplicate(Arity, '_')),
@@ -525,9 +580,14 @@ do_read_disc_schema(Fname, Keep) ->
Res.
get_initial_schema(SchemaStorage, Nodes) ->
+ get_initial_schema(SchemaStorage, Nodes, []).
+
+get_initial_schema(SchemaStorage, Nodes, Properties) -> %
+ UserProps = initial_schema_properties(Properties),
Cs = #cstruct{name = schema,
record_name = schema,
- attributes = [table, cstruct]},
+ attributes = [table, cstruct],
+ user_properties = UserProps},
Cs2 =
case SchemaStorage of
ram_copies -> Cs#cstruct{ram_copies = Nodes};
@@ -535,6 +595,35 @@ get_initial_schema(SchemaStorage, Nodes) ->
end,
cs2list(Cs2).
+initial_schema_properties(Props0) ->
+ DefaultProps = remove_duplicates(mnesia_monitor:get_env(schema)),
+ Props = lists:foldl(
+ fun({K,V}, Acc) ->
+ lists:keystore(K, 1, Acc, {K,V})
+ end, DefaultProps, remove_duplicates(Props0)),
+ initial_schema_properties_(Props).
+
+initial_schema_properties_([{backend_types, Types}|Props]) ->
+ lists:foreach(fun({Name, Module}) ->
+ verify_backend_type(Name, Module)
+ end, Types),
+ [{mnesia_backend_types, Types}|initial_schema_properties_(Props)];
+initial_schema_properties_([{index_plugins, Plugins}|Props]) ->
+ lists:foreach(fun({Name, Module, Function}) ->
+ verify_index_plugin(Name, Module, Function)
+ end, Plugins),
+ [{mnesia_index_plugins, Plugins}|initial_schema_properties_(Props)];
+initial_schema_properties_([P|_Props]) ->
+ mnesia:abort({bad_schema_property, P});
+initial_schema_properties_([]) ->
+ [].
+
+remove_duplicates([{K,_} = H|T]) ->
+ [H | remove_duplicates([X || {K1,_} = X <- T,
+ K1 =/= K])];
+remove_duplicates([]) ->
+ [].
+
read_cstructs_from_disc() ->
%% Assumptions:
%% - local schema lock in global
@@ -551,8 +640,9 @@ read_cstructs_from_disc() ->
{type, set}],
case dets:open_file(make_ref(), Args) of
{ok, Tab} ->
+ ExtTypes = get_ext_types_disc(),
Fun = fun({_, _, List}) ->
- {continue, list2cs(List)}
+ {continue, list2cs(List, ExtTypes)}
end,
Cstructs = dets:traverse(Tab, Fun),
dets:close(Tab),
@@ -632,7 +722,7 @@ do_insert_schema_ops(_Store, []) ->
api_list2cs(List) when is_list(List) ->
Name = pick(unknown, name, List, must),
- Keys = check_keys(Name, List, record_info(fields, cstruct)),
+ Keys = check_keys(Name, List),
check_duplicates(Name, Keys),
list2cs(List);
api_list2cs(Other) ->
@@ -647,7 +737,15 @@ cs2list(Cs) when is_record(Cs, cstruct) ->
cs2list(CreateList) when is_list(CreateList) ->
CreateList;
-%% since 4.6
+cs2list(Cs) when element(1, Cs) == cstruct, tuple_size(Cs) == 20 ->
+ Tags = [name,type,
+ ram_copies,disc_copies,disc_only_copies,external_copies,
+ load_order,access_mode,majority,index,snmp,local_content,
+ record_name,attributes,
+ user_properties,frag_properties,storage_properties,
+ cookie,version],
+ rec2list(Tags, Tags, 2, Cs);
+%% since vsn-4.6 (protocol 8.2 or older)
cs2list(Cs) when element(1, Cs) == cstruct, tuple_size(Cs) == 19 ->
Tags = [name,type,ram_copies,disc_copies,disc_only_copies,
load_order,access_mode,majority,index,snmp,local_content,
@@ -657,8 +755,38 @@ cs2list(Cs) when element(1, Cs) == cstruct, tuple_size(Cs) == 19 ->
rec2list(Tags, Tags, 2, Cs).
cs2list(false, Cs) ->
- cs2list(Cs).
+ cs2list(Cs);
+cs2list({8,3}, Cs) ->
+ cs2list(Cs);
+cs2list({8,Minor}, Cs) when Minor =:= 2; Minor =:= 1 ->
+ Orig = record_info(fields, cstruct),
+ Tags = [name,type,ram_copies,disc_copies,disc_only_copies,
+ load_order,access_mode,majority,index,snmp,local_content,
+ record_name,attributes,
+ user_properties,frag_properties,storage_properties,
+ cookie,version],
+ CsList = rec2list(Tags, Orig, 2, Cs),
+ case proplists:get_value(index, CsList, []) of
+ [] -> CsList;
+ NewFormat ->
+ OldFormat = [Pos || {Pos, _Pref} <- NewFormat],
+ lists:keyreplace(index, 1, CsList, {index, OldFormat})
+ end.
+rec2list([index | Tags], [index|Orig], Pos, Rec) ->
+ Val = element(Pos, Rec),
+ [{index, lists:map(
+ fun({_, _Type}=P) -> P;
+ (P) when is_integer(P); is_atom(P) -> {P, ordered}
+ end, Val)} | rec2list(Tags, Orig, Pos + 1, Rec)];
+rec2list([external_copies | Tags], Orig0, Pos, Rec) ->
+ Orig = case Orig0 of
+ [external_copies|Rest] -> Rest;
+ _ -> Orig0
+ end,
+ Val = element(Pos, Rec),
+ [{Alias, Ns} || {{Alias,_}, Ns} <- Val]
+ ++ rec2list(Tags, Orig, Pos+1, Rec);
rec2list([Tag | Tags], [Tag | Orig], Pos, Rec) ->
Val = element(Pos, Rec),
[{Tag, Val} | rec2list(Tags, Orig, Pos + 1, Rec)];
@@ -667,17 +795,33 @@ rec2list([], _, _Pos, _Rec) ->
rec2list(Tags, [_|Orig], Pos, Rec) ->
rec2list(Tags, Orig, Pos+1, Rec).
-normalize_cs(Cstructs, _Node) ->
- Cstructs.
+normalize_cs(Cstructs, Node) ->
+ %% backward-compatibility hack; normalize before returning
+ case need_old_cstructs([Node]) of
+ false ->
+ Cstructs;
+ Version ->
+ %% some other format
+ [convert_cs(Version, Cs) || Cs <- Cstructs]
+ end.
+
+convert_cs(Version, Cs) ->
+ Fields = [Value || {_, Value} <- cs2list(Version, Cs)],
+ list_to_tuple([cstruct|Fields]).
+
+list2cs(List) ->
+ list2cs(List, get_ext_types()).
-list2cs(List) when is_list(List) ->
+list2cs(List, ExtTypes) when is_list(List) ->
Name = pick(unknown, name, List, must),
Type = pick(Name, type, List, set),
Rc0 = pick(Name, ram_copies, List, []),
Dc = pick(Name, disc_copies, List, []),
Doc = pick(Name, disc_only_copies, List, []),
- Rc = case {Rc0, Dc, Doc} of
- {[], [], []} -> [node()];
+
+ Ext = pick_external_copies(List, ExtTypes),
+ Rc = case {Rc0, Dc, Doc, Ext} of
+ {[], [], [], []} -> [node()];
_ -> Rc0
end,
LC = pick(Name, local_content, List, false),
@@ -695,8 +839,6 @@ list2cs(List) when is_list(List) ->
Ix = pick(Name, index, List, []),
verify({alt, [nil, list]}, mnesia_lib:etype(Ix),
{bad_type, Name, {index, [Ix]}}),
- Ix2 = [attr_to_pos(I, Attrs) || I <- Ix],
-
Frag = pick(Name, frag_properties, List, []),
verify({alt, [nil, list]}, mnesia_lib:etype(Frag),
{badarg, Name, {frag_properties, Frag}}),
@@ -723,24 +865,48 @@ list2cs(List) when is_list(List) ->
DetsOpts = proplists:get_value(dets, BEProps, []),
is_list(DetsOpts) orelse mnesia:abort({badarg, Name, {dets, DetsOpts}}),
[CheckProp(Prop, BadDetsOpts) || Prop <- DetsOpts],
- #cstruct{name = Name,
- ram_copies = Rc,
- disc_copies = Dc,
- disc_only_copies = Doc,
- type = Type,
- index = Ix2,
- snmp = Snmp,
- load_order = LoadOrder,
- access_mode = AccessMode,
- majority = Majority,
- local_content = LC,
- record_name = RecName,
- attributes = Attrs,
- user_properties = lists:sort(UserProps),
- frag_properties = lists:sort(Frag),
- storage_properties = lists:sort(BEProps),
- cookie = Cookie,
- version = Version}.
+
+ case lists:keymember(mnesia, 1, application:which_applications()) of
+ true ->
+ Keys = check_keys(Name, List),
+ check_duplicates(Name, Keys);
+ false ->
+ %% check_keys/2 cannot be executed when mnesia is not
+ %% running, due to it not being possible to read what ext
+ %% backends are loaded.
+ %%% this doesn't work - disabled for now:
+ %%%Keys = check_keys(Name, List, record_info(fields, cstruct)),
+ %%%check_duplicates(Name, Keys)
+ ignore
+ end,
+
+ Cs0 = #cstruct{name = Name,
+ ram_copies = Rc,
+ disc_copies = Dc,
+ disc_only_copies = Doc,
+ external_copies = Ext,
+ type = Type,
+ index = Ix,
+ snmp = Snmp,
+ load_order = LoadOrder,
+ access_mode = AccessMode,
+ majority = Majority,
+ local_content = LC,
+ record_name = RecName,
+ attributes = Attrs,
+ user_properties = lists:sort(UserProps),
+ frag_properties = lists:sort(Frag),
+ storage_properties = lists:sort(BEProps),
+ cookie = Cookie,
+ version = Version},
+ case Ix of
+ [] -> Cs0;
+ [_|_] ->
+ Ix2 = expand_index_attrs(Cs0),
+ Cs0#cstruct{index = Ix2}
+ end;
+list2cs(Other, _ExtTypes) ->
+ mnesia:abort({badarg, Other}).
pick(Tab, Key, List, Default) ->
case lists:keysearch(Key, 1, List) of
@@ -754,6 +920,79 @@ pick(Tab, Key, List, Default) ->
mnesia:abort({bad_type, Tab, BadArg})
end.
+pick_external_copies(_List, []) ->
+ [];
+pick_external_copies(List, ExtTypes) ->
+ lists:foldr(
+ fun({K, Val}, Acc) ->
+ case lists:keyfind(K, 1, ExtTypes) of
+ false ->
+ Acc;
+ {_, Mod} ->
+ [{{K,Mod}, Val}|Acc]
+ end
+ end, [], List).
+
+expand_storage_type(S) when S==ram_copies;
+ S==disc_copies;
+ S==disc_only_copies ->
+ S;
+expand_storage_type(S) ->
+ case lists:keyfind(S, 1, get_ext_types()) of
+ false ->
+ mnesia:abort({bad_type, {storage_type, S}});
+ {Alias, Mod} ->
+ {ext, Alias, Mod}
+ end.
+
+get_ext_types() ->
+ get_schema_user_property(mnesia_backend_types).
+
+get_index_plugins() ->
+ get_schema_user_property(mnesia_index_plugins).
+
+get_schema_user_property(Key) ->
+ Tab = schema,
+ %% Must work reliably both within transactions and outside of transactions
+ Res = case get(mnesia_activity_state) of
+ undefined ->
+ dirty_read_table_property(Tab, Key);
+ _ ->
+ do_read_table_property(Tab, Key)
+ end,
+ case Res of
+ undefined ->
+ [];
+ {_, Types} ->
+ Types
+ end.
+
+get_ext_types_disc() ->
+ try get_ext_types_disc_()
+ catch
+ error:_ ->[]
+ end.
+
+get_ext_types_disc_() ->
+ case mnesia_schema:remote_read_schema() of
+ {ok, _, Prop} ->
+ K1 = user_properties,
+ case lists:keyfind(K1, 1, Prop) of
+ {K1, UserProp} ->
+ K2 = mnesia_backend_types,
+ case lists:keyfind(K2, 1, UserProp) of
+ {K2, Types} ->
+ Types;
+ _ ->
+ []
+ end;
+ _ ->
+ []
+ end;
+ _ ->
+ []
+ end.
+
%% Convert attribute name to integer if neccessary
attr_tab_to_pos(_Tab, Pos) when is_integer(Pos) ->
Pos;
@@ -761,6 +1000,7 @@ attr_tab_to_pos(Tab, Attr) ->
attr_to_pos(Attr, val({Tab, attributes})).
%% Convert attribute name to integer if neccessary
+attr_to_pos({_} = P, _) -> P;
attr_to_pos(Pos, _Attrs) when is_integer(Pos) ->
Pos;
attr_to_pos(Attr, Attrs) when is_atom(Attr) ->
@@ -775,8 +1015,18 @@ attr_to_pos(Attr, [_ | Attrs], Pos) ->
attr_to_pos(Attr, _, _) ->
mnesia:abort({bad_type, Attr}).
+check_keys(Tab, Attrs) ->
+ Types = [T || {T,_} <- get_ext_types()],
+ check_keys(Tab, Attrs, Types ++ record_info(fields, cstruct)).
+
check_keys(Tab, [{Key, _Val} | Tail], Items) ->
- case lists:member(Key, Items) of
+ Key1 = if
+ is_tuple(Key) ->
+ element(1, Key);
+ true ->
+ Key
+ end,
+ case lists:member(Key1, Items) of
true -> [Key | check_keys(Tab, Tail, Items)];
false -> mnesia:abort({badarg, Tab, Key})
end;
@@ -800,7 +1050,92 @@ has_duplicates([]) ->
false.
%% This is the only place where we check the validity of data
-verify_cstruct(Cs) when is_record(Cs, cstruct) ->
+
+verify_cstruct(#cstruct{} = Cs) ->
+ assert_correct_cstruct(Cs),
+ Cs1 = verify_external_copies(
+ Cs#cstruct{index = expand_index_attrs(Cs)}),
+ assert_correct_cstruct(Cs1),
+ Cs1.
+
+expand_index_attrs(#cstruct{index = Ix, attributes = Attrs,
+ name = Tab} = Cs) ->
+ Prefered = prefered_index_types(Cs),
+ expand_index_attrs(Ix, Tab, Attrs, Prefered).
+
+expand_index_attrs(Ix, Tab, Attrs, Prefered) ->
+ lists:map(fun(P) when is_integer(P); is_atom(P) ->
+ {attr_to_pos(P, Attrs), Prefered};
+ ({A} = P) when is_atom(A) ->
+ {P, Prefered};
+ ({P, Type}) ->
+ {attr_to_pos(P, Attrs), Type};
+ (_Other) ->
+ mnesia:abort({bad_type, Tab, {index, Ix}})
+ end, Ix).
+
+prefered_index_types(#cstruct{external_copies = Ext}) ->
+ ExtTypes = [mnesia_lib:semantics(S, index_types) ||
+ {S,Ns} <- Ext, Ns =/= []],
+ case intersect_types(ExtTypes) of
+ [] -> ordered;
+ [Pref|_] -> Pref
+ end.
+
+intersect_types([]) ->
+ [];
+intersect_types([S1, S2|Rest]) ->
+ intersect_types([S1 -- (S1 -- S2)|Rest]);
+intersect_types([S]) ->
+ S.
+
+verify_external_copies(#cstruct{external_copies = []} = Cs) ->
+ Cs;
+verify_external_copies(#cstruct{name = Tab, external_copies = EC} = Cs) ->
+ Bad = {bad_type, Tab, {external_copies, EC}},
+ AllECNodes = lists:concat([Ns || {_, Ns} <- EC,
+ is_list(Ns)]),
+ verify(true, length(lists:usort(AllECNodes)) == length(AllECNodes), Bad),
+ CsL = cs2list(Cs),
+ CsL1 = lists:foldl(
+ fun({{Alias, Mod}, Ns} = _X, CsLx) ->
+ BadTab = fun(Why) ->
+ {Why, Tab, {{ext, Alias, Mod},Ns}}
+ end,
+ verify(atom, mnesia_lib:etype(Mod), BadTab),
+ verify(true, fun() ->
+ lists:all(fun is_atom/1, Ns)
+ end, BadTab),
+ check_semantics(Mod, Alias, BadTab, Cs),
+ try Mod:check_definition(Alias, Tab, Ns, CsLx) of
+ ok ->
+ CsLx;
+ {ok, CsLx1} ->
+ CsLx1;
+ {error, Reason} ->
+ mnesia:abort(BadTab(Reason))
+ catch
+ error:E ->
+ mnesia:abort(BadTab(E))
+ end;
+ (_, CsLx) ->
+ CsLx
+ end, CsL, EC),
+ list2cs(CsL1).
+
+check_semantics(Mod, Alias, BadTab, #cstruct{type = Type}) ->
+ Ext = {ext, Alias, Mod},
+ case lists:member(mnesia_lib:semantics(Ext, storage), [ram_copies, disc_copies,
+ disc_only_copies]) of
+ false -> mnesia:abort(BadTab(invalid_storage));
+ true -> ok
+ end,
+ case lists:member(Type, mnesia_lib:semantics(Ext, types)) of
+ false -> mnesia:abort(BadTab(bad_type));
+ true -> ok
+ end.
+
+assert_correct_cstruct(Cs) when is_record(Cs, cstruct) ->
verify_nodes(Cs),
Tab = Cs#cstruct.name,
@@ -841,22 +1176,30 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) ->
Attrs),
Index = Cs#cstruct.index,
+
verify({alt, [nil, list]}, mnesia_lib:etype(Index),
{bad_type, Tab, {index, Index}}),
+ IxPlugins = get_index_plugins(),
+ AllowIndexOnKey = check_if_allow_index_on_key(),
IxFun =
- fun(Pos) ->
- verify(true, fun() ->
- if
- is_integer(Pos),
- Pos > 2,
- Pos =< Arity ->
- true;
- true -> false
- end
- end,
- {bad_type, Tab, {index, [Pos]}})
- end,
+ fun(Pos) ->
+ verify(
+ true, fun() ->
+ I = index_pos(Pos),
+ case Pos of
+ {_, T} ->
+ (T==bag orelse T==ordered)
+ andalso good_ix_pos(
+ I, AllowIndexOnKey,
+ Arity, IxPlugins);
+ _ ->
+ good_ix_pos(Pos, AllowIndexOnKey,
+ Arity, IxPlugins)
+ end
+ end,
+ {bad_type, Tab, {index, [Pos]}})
+ end,
lists:foreach(IxFun, Index),
LC = Cs#cstruct.local_content,
@@ -880,7 +1223,9 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) ->
{badarg, Tab, {snmp, Snmp}}),
CheckProp = fun(Prop) when is_tuple(Prop), size(Prop) >= 1 -> ok;
- (Prop) -> mnesia:abort({bad_type, Tab, {user_properties, [Prop]}})
+ (Prop) ->
+ mnesia:abort({bad_type, Tab,
+ {user_properties, [Prop]}})
end,
lists:foreach(CheckProp, Cs#cstruct.user_properties),
@@ -900,17 +1245,45 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) ->
mnesia:abort({bad_type, Tab, {version, Version}})
end.
+good_ix_pos({_} = P, _, _, Plugins) ->
+ lists:keymember(P, 1, Plugins);
+good_ix_pos(I, true, Arity, _) when is_integer(I) ->
+ I >= 0 andalso I =< Arity;
+good_ix_pos(I, false, Arity, _) when is_integer(I) ->
+ I > 2 andalso I =< Arity;
+good_ix_pos(_, _, _, _) ->
+ false.
+
+
+check_if_allow_index_on_key() ->
+ case mnesia_monitor:get_env(allow_index_on_key) of
+ true ->
+ true;
+ _ ->
+ false
+ end.
+
verify_nodes(Cs) ->
Tab = Cs#cstruct.name,
Ram = Cs#cstruct.ram_copies,
Disc = Cs#cstruct.disc_copies,
DiscOnly = Cs#cstruct.disc_only_copies,
+ Ext = lists:append([Ns || {_,Ns} <- Cs#cstruct.external_copies]),
LoadOrder = Cs#cstruct.load_order,
verify({alt, [nil, list]}, mnesia_lib:etype(Ram),
{bad_type, Tab, {ram_copies, Ram}}),
verify({alt, [nil, list]}, mnesia_lib:etype(Disc),
{bad_type, Tab, {disc_copies, Disc}}),
+ lists:foreach(
+ fun({BE, Ns}) ->
+ verify({alt, [nil, list]}, mnesia_lib:etype(Ns),
+ {bad_type, Tab, {BE, Ns}}),
+ lists:foreach(fun(N) ->
+ verify(atom, mnesia_lib:etype(N),
+ {bad_type, Tab, {BE, Ns}})
+ end, Ns)
+ end, Cs#cstruct.external_copies),
case Tab of
schema ->
verify([], DiscOnly, {bad_type, Tab, {disc_only_copies, DiscOnly}});
@@ -922,12 +1295,15 @@ verify_nodes(Cs) ->
verify(integer, mnesia_lib:etype(LoadOrder),
{bad_type, Tab, {load_order, LoadOrder}}),
- Nodes = Ram ++ Disc ++ DiscOnly,
+ Nodes = Ram ++ Disc ++ DiscOnly ++ Ext,
verify(list, mnesia_lib:etype(Nodes),
{combine_error, Tab,
- [{ram_copies, []}, {disc_copies, []}, {disc_only_copies, []}]}),
+ [{ram_copies, []}, {disc_copies, []},
+ {disc_only_copies, []}, {external_copies, []}]}),
verify(false, has_duplicates(Nodes), {combine_error, Tab, Nodes}),
- AtomCheck = fun(N) -> verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N}) end,
+ AtomCheck = fun(N) ->
+ verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N})
+ end,
lists:foreach(AtomCheck, Nodes).
verify(Expected, Fun, Error) when is_function(Fun) ->
@@ -1010,28 +1386,194 @@ check_active([{badrpc, Reason} | _Replies], Expl, Tab) ->
check_active([], _Expl, _Tab) ->
ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function for definining an external backend type
+
+add_backend_type(Name, Module) ->
+ case schema_transaction(fun() -> do_add_backend_type(Name, Module) end) of
+ {atomic, NeedsInit} ->
+ case NeedsInit of
+ true ->
+ Module:init_backend();
+ false ->
+ ignore
+ end,
+ Module:add_aliases([Name]),
+ {atomic, ok};
+ Other ->
+ Other
+ end.
+
+do_add_backend_type(Name, Module) ->
+ verify_backend_type(Name, Module),
+ Types = case do_read_table_property(schema, mnesia_backend_types) of
+ undefined ->
+ [];
+ {_, Ts} ->
+ case lists:keymember(Name, 1, Ts) of
+ true ->
+ mnesia:abort({backend_type_already_exists, Name});
+ false ->
+ Ts
+ end
+ end,
+ ModuleRegistered = lists:keymember(Module, 2, Types),
+ do_write_table_property(schema, {mnesia_backend_types,
+ [{Name, Module}|Types]}),
+ ModuleRegistered.
+
+delete_backend_type(Name) ->
+ schema_transaction(fun() -> do_delete_backend_type(Name) end).
+
+do_delete_backend_type(Name) ->
+ case do_read_table_property(schema, mnesia_backend_types) of
+ undefined ->
+ [];
+ {_, Ts} ->
+ case lists:keyfind(Name, 1, Ts) of
+ {_, Mod} ->
+ case using_backend_type(Name, Mod) of
+ [_|_] = Tabs ->
+ mnesia:abort({backend_in_use, {Name, Tabs}});
+ [] ->
+ do_write_table_property(
+ schema, {mnesia_backend_types,
+ lists:keydelete(Name, 1, Ts)})
+ end;
+ false ->
+ mnesia:abort({no_such_backend, Name})
+ end
+ end.
+
+using_backend_type(Name, Mod) ->
+ Ext = ets:select(mnesia_gvar,
+ [{ {{'$1',external_copies},'$2'}, [], [{{'$1','$2'}}] }]),
+ Entry = {Name, Mod},
+ [T || {T,C} <- Ext,
+ lists:keymember(Entry, 1, C)].
+
+verify_backend_type(Name, Module) ->
+ case legal_backend_name(Name) of
+ false ->
+ mnesia:abort({bad_type, {backend_type,Name,Module}});
+ true ->
+ ok
+ end,
+ ExpectedExports = mnesia_backend_type:behaviour_info(callbacks),
+ Exports = try Module:module_info(exports)
+ catch
+ error:_ ->
+ mnesia:abort({undef_backend, Module})
+ end,
+ case ExpectedExports -- Exports of
+ [] ->
+ ok;
+ _Other ->
+ io:fwrite(user, "Missing backend_type exports: ~p~n", [_Other]),
+ mnesia:abort({bad_type, {backend_type,Name,Module}})
+ end.
+
+legal_backend_name(Name) ->
+ is_atom(Name) andalso
+ (not lists:member(Name, record_info(fields, cstruct))).
+
+%% Used e.g. by mnesia:system_info(backend_types).
+backend_types() ->
+ [ram_copies, disc_copies, disc_only_copies |
+ [T || {T,_} <- get_ext_types()]].
+
+add_index_plugin(Name, Module, Function) ->
+ schema_transaction(
+ fun() -> do_add_index_plugin(Name, Module, Function) end).
+
+do_add_index_plugin(Name, Module, Function) ->
+ verify_index_plugin(Name, Module, Function),
+ Plugins = case do_read_table_property(schema, mnesia_index_plugins) of
+ undefined ->
+ [];
+ {_, Ps} ->
+ case lists:keymember(Name, 1, Ps) of
+ true ->
+ mnesia:abort({index_plugin_already_exists, Name});
+ false ->
+ Ps
+ end
+ end,
+ do_write_table_property(schema, {mnesia_index_plugins,
+ [{Name, Module, Function}|Plugins]}).
+
+delete_index_plugin(P) ->
+ schema_transaction(
+ fun() -> do_delete_index_plugin(P) end).
+
+do_delete_index_plugin({A} = P) when is_atom(A) ->
+ Plugins = get_index_plugins(),
+ case lists:keyfind(P, 1, Plugins) of
+ false ->
+ mnesia:abort({no_exists, {index_plugin, P}});
+ _Found ->
+ case ets:select(mnesia_gvar,
+ [{ {{'$1',{index,{P,'_'}}},'_'},[],['$1']},
+ { {{'$1',{index,P}},'_'},[],['$1']}], 1) of
+ {[_], _} ->
+ mnesia:abort({plugin_in_use, P});
+ '$end_of_table' ->
+ do_write_table_property(
+ schema, {mnesia_index_plugins,
+ lists:keydelete(P, 1, Plugins)})
+ end
+ end.
+
+verify_index_plugin({A} = Name, Module, Function)
+ when is_atom(A), is_atom(Module), is_atom(Function) ->
+ case code:ensure_loaded(Module) of
+ {error, nofile} ->
+ mnesia:abort({bad_type, {index_plugin,Name,Module,Function}});
+ {module,_} ->
+ %% Index plugins are called as Module:Function(Tab, Pos, Obj)
+ case erlang:function_exported(Module, Function, 3) of
+ true ->
+ ok;
+ false ->
+ mnesia:abort(
+ {bad_type, {index_plugin,Name,Module,Function}})
+ end
+ end;
+verify_index_plugin(Name, Module, Function) ->
+ mnesia:abort({bad_type, {index_plugin,Name,Module,Function}}).
+
+
+%% Used e.g. by mnesia:system_info(backend_types).
+index_plugins() ->
+ get_index_plugins().
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Here's the real interface function to create a table
-create_table(TabDef) ->
- schema_transaction(fun() -> do_multi_create_table(TabDef) end).
+create_table([_|_] = TabDef) ->
+ schema_transaction(fun() -> do_multi_create_table(TabDef) end);
+create_table(Arg) -> {aborted, {badarg, Arg}}.
%% And the corresponding do routines ....
do_multi_create_table(TabDef) ->
get_tid_ts_and_lock(schema, write),
ensure_writable(schema),
+ do_create_table(TabDef),
+ ok.
+
+do_create_table(TabDef) when is_list(TabDef) ->
Cs = api_list2cs(TabDef),
case Cs#cstruct.frag_properties of
[] ->
- do_create_table(Cs);
+ do_create_table_1(Cs);
_Props ->
CsList = mnesia_frag:expand_cstruct(Cs),
- lists:foreach(fun do_create_table/1, CsList)
- end,
- ok.
+ lists:foreach(fun do_create_table_1/1, CsList)
+ end.
-do_create_table(Cs) ->
+do_create_table_1(Cs) ->
{_Mod, _Tid, Ts} = get_tid_ts_and_lock(schema, none),
Store = Ts#tidstore.store,
do_insert_schema_ops(Store, make_create_table(Cs)).
@@ -1041,14 +1583,9 @@ make_create_table(Cs) ->
verify(false, check_if_exists(Tab), {already_exists, Tab}),
unsafe_make_create_table(Cs).
-% unsafe_do_create_table(Cs) ->
-% {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
-% Store = Ts#tidstore.store,
-% do_insert_schema_ops(Store, unsafe_make_create_table(Cs)).
-
-unsafe_make_create_table(Cs) ->
+unsafe_make_create_table(Cs0) ->
{_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
- verify_cstruct(Cs),
+ Cs = verify_cstruct(Cs0),
Tab = Cs#cstruct.name,
%% Check that we have all disc replica nodes running
@@ -1196,8 +1733,7 @@ make_add_table_copy(Tab, Node, Storage) ->
Cs = incr_version(val({Tab, cstruct})),
Ns = mnesia_lib:cs_to_nodes(Cs),
verify(false, lists:member(Node, Ns), {already_exists, Tab, Node}),
- Cs2 = new_cs(Cs, Node, Storage, add),
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(new_cs(Cs, Node, Storage, add)),
%% Check storage and if node is running
IsRunning = lists:member(Node, val({current, db_nodes})),
@@ -1245,14 +1781,14 @@ make_del_table_copy(Tab, Node) ->
_ when Tab == schema ->
%% ensure_active(Cs2),
ensure_not_active(Tab, Node),
- verify_cstruct(Cs2),
+ Cs3 = verify_cstruct(Cs2),
Ops = remove_node_from_tabs(val({schema, tables}), Node),
- [{op, del_table_copy, ram_copies, Node, vsn_cs2list(Cs2)} | Ops];
+ [{op, del_table_copy, ram_copies, Node, vsn_cs2list(Cs3)} | Ops];
_ ->
ensure_active(Cs),
- verify_cstruct(Cs2),
- get_tid_ts_and_lock(Tab, write),
- [{op, del_table_copy, Storage, Node, vsn_cs2list(Cs2)}]
+ Cs3 = verify_cstruct(Cs2),
+ get_tid_ts_and_lock(Tab, write),
+ [{op, del_table_copy, Storage, Node, vsn_cs2list(Cs3)}]
end.
remove_node_from_tabs([], _Node) ->
@@ -1278,9 +1814,9 @@ remove_node_from_tabs([Tab|Rest], Node) ->
[{op, delete_table, vsn_cs2list(Cs)} |
remove_node_from_tabs(Rest, Node)];
_Ns ->
- verify_cstruct(Cs2),
+ Cs3 = verify_cstruct(Cs2),
get_tid_ts_and_lock(Tab, write),
- [{op, del_table_copy, ram_copies, Node, vsn_cs2list(Cs2)}|
+ [{op, del_table_copy, ram_copies, Node, vsn_cs2list(Cs3)}|
remove_node_from_tabs(Rest, Node)]
end
end.
@@ -1298,8 +1834,34 @@ new_cs(Cs, Node, disc_copies, del) ->
new_cs(Cs, Node, disc_only_copies, del) ->
Cs#cstruct{disc_only_copies =
lists:delete(Node , Cs#cstruct.disc_only_copies)};
-new_cs(Cs, _Node, Storage, _Op) ->
- mnesia:abort({badarg, Cs#cstruct.name, Storage}).
+new_cs(#cstruct{external_copies = ExtCps} = Cs, Node, Storage0, Op) ->
+ Storage = case Storage0 of
+ {ext, Alias, _} -> Alias;
+ Alias -> Alias
+ end,
+ ExtTypes = get_ext_types(),
+ case lists:keyfind(Storage, 1, ExtTypes) of
+ false ->
+ mnesia:abort({badarg, Cs#cstruct.name, Storage});
+ {_, Mod} ->
+ Key = {Storage, Mod},
+ case {lists:keymember(Key, 1, ExtCps), Op} of
+ {false, del} ->
+ Cs;
+ {false, add} ->
+ Cs#cstruct{external_copies = [{Key, [Node]}|ExtCps]};
+ {true, _} ->
+ F = fun({K, Ns}) when K == Key ->
+ case Op of
+ del -> {K, lists:delete(Node, Ns)};
+ add -> {K, opt_add(Node, Ns)}
+ end;
+ (X) ->
+ X
+ end,
+ Cs#cstruct{external_copies = lists:map(F, ExtCps)}
+ end
+ end.
opt_add(N, L) -> [N | lists:delete(N, L)].
@@ -1335,8 +1897,7 @@ make_move_table(Tab, FromNode, ToNode) ->
verify(true, lists:member(ToNode, Running), {not_active, schema, ToNode}),
Cs2 = new_cs(Cs, ToNode, Storage, add),
- Cs3 = new_cs(Cs2, FromNode, Storage, del),
- verify_cstruct(Cs3),
+ Cs3 = verify_cstruct(new_cs(Cs2, FromNode, Storage, del)),
[{op, add_table_copy, Storage, ToNode, vsn_cs2list(Cs2)},
{op, sync_trans},
{op, del_table_copy, Storage, FromNode, vsn_cs2list(Cs3)}].
@@ -1363,9 +1924,11 @@ make_change_table_copy_type(Tab, Node, ToS) ->
Cs = incr_version(val({Tab, cstruct})),
FromS = mnesia_lib:storage_type_at_node(Node, Tab),
- case compare_storage_type(false, FromS, ToS) of
+ ToSExp = expand_storage_type(ToS),
+
+ case compare_storage_type(false, FromS, ToSExp) of
{same, _} ->
- mnesia:abort({already_exists, Tab, Node, ToS});
+ mnesia:abort({already_exists, Tab, Node, ToSExp});
{diff, _} ->
ignore;
incompatible ->
@@ -1373,10 +1936,8 @@ make_change_table_copy_type(Tab, Node, ToS) ->
end,
Cs2 = new_cs(Cs, Node, FromS, del),
- Cs3 = new_cs(Cs2, Node, ToS, add),
- verify_cstruct(Cs3),
-
- [{op, change_table_copy_type, Node, FromS, ToS, vsn_cs2list(Cs3)}].
+ Cs3 = verify_cstruct(new_cs(Cs2, Node, ToS, add)),
+ [{op, change_table_copy_type, Node, FromS, ToSExp, vsn_cs2list(Cs3)}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% change index functions ....
@@ -1398,11 +1959,12 @@ make_add_table_index(Tab, Pos) ->
Cs = incr_version(val({Tab, cstruct})),
ensure_active(Cs),
Ix = Cs#cstruct.index,
- verify(false, lists:member(Pos, Ix), {already_exists, Tab, Pos}),
+ verify(false, lists:keymember(index_pos(Pos), 1, Ix),
+ {already_exists, Tab, Pos}),
Ix2 = lists:sort([Pos | Ix]),
- Cs2 = Cs#cstruct{index = Ix2},
- verify_cstruct(Cs2),
- [{op, add_index, Pos, vsn_cs2list(Cs2)}].
+ Cs2 = verify_cstruct(Cs#cstruct{index = Ix2}),
+ NewPosInfo = lists:keyfind(Pos, 1, Cs2#cstruct.index),
+ [{op, add_index, NewPosInfo, vsn_cs2list(Cs2)}].
del_table_index(Tab, Pos) ->
schema_transaction(fun() -> do_del_table_index(Tab, Pos) end).
@@ -1420,9 +1982,8 @@ make_del_table_index(Tab, Pos) ->
Cs = incr_version(val({Tab, cstruct})),
ensure_active(Cs),
Ix = Cs#cstruct.index,
- verify(true, lists:member(Pos, Ix), {no_exists, Tab, Pos}),
- Cs2 = Cs#cstruct{index = lists:delete(Pos, Ix)},
- verify_cstruct(Cs2),
+ verify(true, lists:keymember(Pos, 1, Ix), {no_exists, Tab, Pos}),
+ Cs2 = verify_cstruct(Cs#cstruct{index = lists:keydelete(Pos, 1, Ix)}),
[{op, del_index, Pos, vsn_cs2list(Cs2)}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1444,8 +2005,7 @@ make_add_snmp(Tab, Ustruct) ->
verify([], Cs#cstruct.snmp, {already_exists, Tab, snmp}),
Error = {badarg, Tab, snmp, Ustruct},
verify(true, mnesia_snmp_hook:check_ustruct(Ustruct), Error),
- Cs2 = Cs#cstruct{snmp = Ustruct},
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(Cs#cstruct{snmp = Ustruct}),
[{op, add_snmp, Ustruct, vsn_cs2list(Cs2)}].
del_snmp(Tab) ->
@@ -1462,8 +2022,7 @@ make_del_snmp(Tab) ->
ensure_writable(schema),
Cs = incr_version(val({Tab, cstruct})),
ensure_active(Cs),
- Cs2 = Cs#cstruct{snmp = []},
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(Cs#cstruct{snmp = []}),
[{op, del_snmp, vsn_cs2list(Cs2)}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1492,19 +2051,21 @@ make_transform(Tab, Fun, NewAttrs, NewRecName) ->
Cs = incr_version(val({Tab, cstruct})),
ensure_active(Cs),
ensure_writable(Tab),
- case mnesia_lib:val({Tab, index}) of
+ case Cs#cstruct.index of
[] ->
- Cs2 = Cs#cstruct{attributes = NewAttrs, record_name = NewRecName},
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(
+ Cs#cstruct{attributes = NewAttrs,
+ record_name = NewRecName}),
[{op, transform, Fun, vsn_cs2list(Cs2)}];
PosList ->
- DelIdx = fun(Pos, Ncs) ->
+ DelIdx = fun({Pos,_}, Ncs) ->
Ix = Ncs#cstruct.index,
- Ncs1 = Ncs#cstruct{index = lists:delete(Pos, Ix)},
+ Ix2 = lists:keydelete(Pos, 1, Ix),
+ Ncs1 = Ncs#cstruct{index = Ix2},
Op = {op, del_index, Pos, vsn_cs2list(Ncs1)},
{Op, Ncs1}
end,
- AddIdx = fun(Pos, Ncs) ->
+ AddIdx = fun({_,_} = Pos, Ncs) ->
Ix = Ncs#cstruct.index,
Ix2 = lists:sort([Pos | Ix]),
Ncs1 = Ncs#cstruct{index = Ix2},
@@ -1514,10 +2075,16 @@ make_transform(Tab, Fun, NewAttrs, NewRecName) ->
{DelOps, Cs1} = lists:mapfoldl(DelIdx, Cs, PosList),
Cs2 = Cs1#cstruct{attributes = NewAttrs, record_name = NewRecName},
{AddOps, Cs3} = lists:mapfoldl(AddIdx, Cs2, PosList),
- verify_cstruct(Cs3),
- lists:flatten([DelOps, {op, transform, Fun, vsn_cs2list(Cs2)}, AddOps])
+ _ = verify_cstruct(Cs3), % just a sanity check
+ lists:flatten([DelOps, {op, transform, Fun, vsn_cs2list(Cs2)},
+ AddOps])
end.
+index_pos({Pos,_}) -> Pos;
+index_pos(Pos) when is_integer(Pos) -> Pos;
+index_pos({P} = Pos) when is_atom(P) -> Pos.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -1537,8 +2104,7 @@ make_change_table_access_mode(Tab, Mode) ->
ensure_active(Cs),
OldMode = Cs#cstruct.access_mode,
verify(false, OldMode == Mode, {already_exists, Tab, Mode}),
- Cs2 = Cs#cstruct{access_mode = Mode},
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(Cs#cstruct{access_mode = Mode}),
[{op, change_table_access_mode, vsn_cs2list(Cs2), OldMode, Mode}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1558,8 +2124,7 @@ make_change_table_load_order(Tab, LoadOrder) ->
Cs = incr_version(val({Tab, cstruct})),
ensure_active(Cs),
OldLoadOrder = Cs#cstruct.load_order,
- Cs2 = Cs#cstruct{load_order = LoadOrder},
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(Cs#cstruct{load_order = LoadOrder}),
[{op, change_table_load_order, vsn_cs2list(Cs2), OldLoadOrder, LoadOrder}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1605,6 +2170,7 @@ write_table_property(Tab, Prop) when is_tuple(Prop), size(Prop) >= 1 ->
schema_transaction(fun() -> do_write_table_property(Tab, Prop) end);
write_table_property(Tab, Prop) ->
{aborted, {bad_type, Tab, Prop}}.
+
do_write_table_property(Tab, Prop) ->
TidTs = get_tid_ts_and_lock(schema, write),
{_, _, Ts} = TidTs,
@@ -1636,8 +2202,7 @@ make_write_table_properties(Tab, [Prop | Props], Cs) ->
PropKey = element(1, Prop),
DelProps = lists:keydelete(PropKey, 1, OldProps),
MergedProps = lists:merge(DelProps, [Prop]),
- Cs2 = Cs#cstruct{user_properties = MergedProps},
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(Cs#cstruct{user_properties = MergedProps}),
[{op, write_property, vsn_cs2list(Cs2), Prop} |
make_write_table_properties(Tab, Props, Cs2)];
make_write_table_properties(_Tab, [], _Cs) ->
@@ -1682,22 +2247,38 @@ do_read_table_property(Tab, Key) ->
{_, _, Ts} = TidTs,
Store = Ts#tidstore.store,
Props = ets:foldl(
- fun({op, create_table, [{name, T}|Opts]}, _Acc)
- when T==Tab ->
+ fun({op, announce_im_running,_,Opts,_,_}, _Acc) when Tab==schema ->
+ find_props(Opts);
+ ({op, create_table, [{name, T}|Opts]}, _Acc)
+ when T==Tab ->
find_props(Opts);
({op, Op, [{name,T}|Opts], _Prop}, _Acc)
- when T==Tab, Op==write_property; Op==delete_property ->
+ when T==Tab, Op==write_property;
+ T==Tab, Op==delete_property ->
find_props(Opts);
({op, delete_table, [{name,T}|_]}, _Acc)
when T==Tab ->
[];
(_Other, Acc) ->
Acc
- end, [], Store),
- case lists:keysearch(Key, 1, Props) of
- {value, Property} ->
- Property;
- false ->
+ end, undefined, Store),
+ case Props of
+ undefined ->
+ get_tid_ts_and_lock(Tab, read),
+ dirty_read_table_property(Tab, Key);
+ _ when is_list(Props) ->
+ case lists:keyfind(Key, 1, Props) of
+ false ->
+ undefined;
+ Other ->
+ Other
+ end
+ end.
+
+dirty_read_table_property(Tab, Key) ->
+ try ets:lookup_element(mnesia_gvar, {Tab,user_property,Key}, 2)
+ catch
+ error:_ ->
undefined
end.
@@ -1757,8 +2338,7 @@ make_delete_table_properties(Tab, PropKeys) ->
make_delete_table_properties(Tab, [PropKey | PropKeys], Cs) ->
OldProps = Cs#cstruct.user_properties,
Props = lists:keydelete(PropKey, 1, OldProps),
- Cs2 = Cs#cstruct{user_properties = Props},
- verify_cstruct(Cs2),
+ Cs2 = verify_cstruct(Cs#cstruct{user_properties = Props}),
[{op, delete_property, vsn_cs2list(Cs2), PropKey} |
make_delete_table_properties(Tab, PropKeys, Cs2)];
make_delete_table_properties(_Tab, [], _Cs) ->
@@ -1899,6 +2479,11 @@ prepare_op(Tid, {op, create_table, TabDef}, _WaitFor) ->
create_disc_only_table(Tab,Cs),
insert_cstruct(Tid, Cs, false),
{true, optional};
+ {ext, Alias, Mod} ->
+ mnesia_lib:set({Tab, create_table},true),
+ create_external_table(Alias, Tab, Mod, Cs),
+ insert_cstruct(Tid, Cs, false),
+ {true, optional};
unknown -> %% No replica on this node
mnesia_lib:set({Tab, create_table},true),
insert_cstruct(Tid, Cs, false),
@@ -1967,6 +2552,12 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
NotActive = mnesia_lib:not_active_here(Tab),
+ if Tab =/= schema ->
+ check_if_disc_required(FromS, ToS);
+ true ->
+ ok
+ end,
+
if
NotActive == true ->
mnesia:abort({not_active, Tab, node()});
@@ -2006,6 +2597,15 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
mnesia:abort({combine_error, Tab, ToS})
end;
+ element(1,FromS) == ext; element(1,ToS) == ext ->
+ if ToS == ram_copies ->
+ create_ram_table(Tab, Cs);
+ true ->
+ ok
+ end,
+ mnesia_dumper:dump_to_logfile(FromS, Tab),
+ mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS);
+
FromS == ram_copies ->
case mnesia_monitor:use_dir() of
true ->
@@ -2021,7 +2621,9 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
disc_only_copies ->
mnesia_dumper:raw_named_dump_table(Tab, dmp)
end,
- mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS)
+ mnesia_checkpoint:tm_change_table_copy_type(Tab,
+ FromS,
+ ToS)
end;
false ->
mnesia:abort({has_no_disc, node()})
@@ -2029,6 +2631,7 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
FromS == disc_copies, ToS == disc_only_copies ->
mnesia_dumper:raw_named_dump_table(Tab, dmp);
+
FromS == disc_only_copies ->
Type = Cs#cstruct.type,
create_ram_table(Tab, Cs),
@@ -2040,6 +2643,7 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
Err = "Failed to copy disc data to ram",
mnesia:abort({system_limit, Tab, {Err,Reason}})
end;
+
true ->
ignore
end,
@@ -2103,6 +2707,8 @@ prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) ->
{true, Objs, mandatory}
catch _:Reason ->
mnesia_lib:db_fixtable(Storage, Tab, false),
+ mnesia_lib:important("Transform function failed: '~p' in '~p'",
+ [Reason, erlang:get_stacktrace()]),
exit({"Bad transform function", Tab, Fun, node(), Reason})
end
end;
@@ -2119,6 +2725,22 @@ prepare_op(_Tid, {op, merge_schema, TabDef}, _WaitFor) ->
prepare_op(_Tid, _Op, _WaitFor) ->
{true, optional}.
+check_if_disc_required(FromS, ToS) ->
+ FromSem = mnesia_lib:semantics(FromS, storage),
+ ToSem = mnesia_lib:semantics(ToS, storage),
+ case {FromSem, ToSem} of
+ {ram_copies, _} when ToSem == disc_copies;
+ ToSem == disc_only_copies ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ ok;
+ false ->
+ mnesia:abort({has_no_disc, node()})
+ end;
+ _ ->
+ ok
+ end.
+
create_ram_table(Tab, #cstruct{type=Type, storage_properties=Props}) ->
EtsOpts = proplists:get_value(ets, Props, []),
Args = [{keypos, 2}, public, named_table, Type | EtsOpts],
@@ -2160,6 +2782,14 @@ create_disc_only_table(Tab, #cstruct{type=Type, storage_properties=Props}) ->
mnesia:abort({system_limit, Tab, {Err,Reason}})
end.
+create_external_table(Alias, Tab, Mod, Cs) ->
+ case mnesia_monitor:unsafe_create_external(Tab, Alias, Mod, Cs) of
+ ok ->
+ ok;
+ {error,Reason} ->
+ Err = "Failed to create external table",
+ mnesia:abort({system_limit, Tab, {Err,Reason}})
+ end.
receive_sync([], Pids) ->
Pids;
@@ -2294,7 +2924,10 @@ undo_prepare_op(Tid, {op, create_table, TabDef}) ->
mnesia_monitor:unsafe_close_dets(Tab),
Dat = mnesia_lib:tab2dat(Tab),
%% disc_delete_table(Tab, Storage),
- file:delete(Dat)
+ file:delete(Dat);
+ {ext, Alias, Mod} ->
+ Mod:close_table(Alias, Tab),
+ Mod:delete_table(Alias, Tab)
end;
undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) ->
@@ -2394,6 +3027,8 @@ ram_delete_table(Tab, Storage) ->
case Storage of
unknown ->
ignore;
+ {ext, _, _} ->
+ ignore;
disc_only_copies ->
ignore;
_Else ->
@@ -2455,13 +3090,27 @@ purge_known_files([File | Tail], KeepFiles, Dir, Suffixes) ->
ignore;
true ->
AbsFile = filename:join([Dir, File]),
- file:delete(AbsFile)
- end
+ delete_recursive(AbsFile)
+ end
end,
purge_known_files(Tail, KeepFiles, Dir, Suffixes);
purge_known_files([], _KeepFiles, _Dir, _Suffixes) ->
ok.
+%% Removes a directory or file recursively
+delete_recursive(Path) ->
+ case filelib:is_dir(Path) of
+ true ->
+ {ok, Names} = file:list_dir(Path),
+ lists:foreach(fun(Name) ->
+ delete_recursive(filename:join(Path, Name))
+ end,
+ Names),
+ file:del_dir(Path);
+ false ->
+ file:delete(Path)
+ end.
+
has_known_suffix(_File, _Suffixes, true) ->
true;
has_known_suffix(File, [Suffix | Tail], false) ->
@@ -2469,11 +3118,33 @@ has_known_suffix(File, [Suffix | Tail], false) ->
has_known_suffix(_File, [], Bool) ->
Bool.
-known_suffixes() -> real_suffixes() ++ tmp_suffixes().
+known_suffixes() -> known_suffixes(get_ext_types_disc()).
+
+known_suffixes(Ext) -> real_suffixes(Ext) ++ tmp_suffixes(Ext).
-real_suffixes() -> [".DAT", ".LOG", ".BUP", ".DCL", ".DCD"].
+real_suffixes(Ext) -> [".DAT", ".LOG", ".BUP", ".DCL", ".DCD"] ++ ext_real_suffixes(Ext).
-tmp_suffixes() -> [".TMP", ".BUPTMP", ".RET", ".DMP"].
+tmp_suffixes() -> tmp_suffixes(get_ext_types_disc()).
+
+tmp_suffixes(Ext) -> [".TMP", ".BUPTMP", ".RET", ".DMP", "."] ++ ext_tmp_suffixes(Ext).
+
+ext_real_suffixes(Ext) ->
+ try lists:foldl(fun(Mod, Acc) -> Acc++Mod:real_suffixes() end, [],
+ [M || {_,M} <- Ext])
+ catch
+ error:E ->
+ verbose("Cant find real ext suffixes (~p)~n", [E]),
+ []
+ end.
+
+ext_tmp_suffixes(Ext) ->
+ try lists:foldl(fun(Mod, Acc) -> Acc++Mod:tmp_suffixes() end, [],
+ [M || {_,M} <- Ext])
+ catch
+ error:E ->
+ verbose("Cant find tmp ext suffixes (~p)~n", [E]),
+ []
+ end.
info() ->
Tabs = lists:sort(val({schema, tables})),
@@ -2591,12 +3262,12 @@ do_restore(R, BupSchema) ->
arrange_restore(R, Fun, Recs) ->
R2 = R#r{insert_op = Fun, recs = Recs},
- case mnesia_bup:iterate(R#r.module, fun restore_items/4, R#r.opaque, R2) of
+ case mnesia_bup:iterate(R#r.module, fun restore_items/5, R#r.opaque, R2) of
{ok, R3} -> R3#r.recs;
{error, Reason} -> mnesia:abort(Reason)
end.
-restore_items([Rec | Recs], Header, Schema, R) ->
+restore_items([Rec | Recs], Header, Schema, Ext, R) ->
Tab = element(1, Rec),
case lists:keysearch(Tab, 1, R#r.tables) of
{value, {Tab, Where0, Snmp, RecName}} ->
@@ -2609,13 +3280,13 @@ restore_items([Rec | Recs], Header, Schema, R) ->
{Rest, NRecs} = restore_tab_items([Rec | Recs], Tab,
RecName, Where, Snmp,
R#r.recs, R#r.insert_op),
- restore_items(Rest, Header, Schema, R#r{recs = NRecs});
+ restore_items(Rest, Header, Schema, Ext, R#r{recs = NRecs});
false ->
Rest = skip_tab_items(Recs, Tab),
- restore_items(Rest, Header, Schema, R)
+ restore_items(Rest, Header, Schema, Ext, R)
end;
-restore_items([], _Header, _Schema, R) ->
+restore_items([], _Header, _Schema, _Ext, R) ->
R.
restore_func(Tab, R) ->
@@ -2629,8 +3300,14 @@ restore_func(Tab, R) ->
where_to_commit(Tab, CsList) ->
Ram = [{N, ram_copies} || N <- pick(Tab, ram_copies, CsList, [])],
Disc = [{N, disc_copies} || N <- pick(Tab, disc_copies, CsList, [])],
- DiscO = [{N, disc_only_copies} || N <- pick(Tab, disc_only_copies, CsList, [])],
- Ram ++ Disc ++ DiscO.
+ DiscO = [{N, disc_only_copies} ||
+ N <- pick(Tab, disc_only_copies, CsList, [])],
+ ExtNodes = [{Alias, Mod, pick(Tab, Alias, CsList, [])} ||
+ {Alias, Mod} <- get_ext_types()],
+ Ext = lists:foldl(fun({Alias, Mod, Ns}, Acc) ->
+ [{N, {ext, Alias, Mod}} || N <- Ns] ++ Acc
+ end, [], ExtNodes),
+ Ram ++ Disc ++ DiscO ++ Ext.
%% Changes of the Meta info of schema itself is not allowed
restore_schema([{schema, schema, _List} | Schema], R) ->
@@ -2709,7 +3386,13 @@ make_dump_tables([schema | _Tabs]) ->
make_dump_tables([Tab | Tabs]) ->
get_tid_ts_and_lock(Tab, read),
TabDef = get_create_list(Tab),
- DiscResident = val({Tab, disc_copies}) ++ val({Tab, disc_only_copies}),
+ DiscResident =
+ val({Tab, disc_copies}) ++
+ val({Tab, disc_only_copies}) ++
+ lists:concat([Ns || {{A,M},Ns} <- val({Tab, external_copies}),
+ lists:member(
+ mnesia_lib:semantics({ext,A,M},storage),
+ [disc_copies, disc_only_copies])]),
verify([], DiscResident,
{"Only allowed on ram_copies", Tab, DiscResident}),
[{op, dump_table, unknown, TabDef} | make_dump_tables(Tabs)];
@@ -2721,7 +3404,9 @@ merge_schema() ->
schema_transaction(fun() -> do_merge_schema([]) end).
merge_schema(UserFun) ->
- schema_transaction(fun() -> UserFun(fun(Arg) -> do_merge_schema(Arg) end) end).
+ schema_transaction(fun() ->
+ UserFun(fun(Arg) -> do_merge_schema(Arg) end)
+ end).
do_merge_schema(LockTabs0) ->
{_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
@@ -2795,11 +3480,23 @@ do_merge_schema(LockTabs0) ->
end.
fetch_cstructs(Node) ->
- rpc:call(Node, mnesia_controller, get_remote_cstructs, []).
+ Convert = mnesia_monitor:needs_protocol_conversion(Node),
+ case rpc:call(Node, mnesia_controller, get_remote_cstructs, []) of
+ {cstructs, Cs0, RemoteRunning1} when Convert ->
+ {cstructs, [list2cs(cs2list(Cs)) || Cs <- Cs0], RemoteRunning1};
+ Result ->
+ Result
+ end.
-need_old_cstructs() -> false.
+need_old_cstructs() ->
+ need_old_cstructs(val({schema, where_to_write})).
-need_old_cstructs(_Nodes) -> false.
+need_old_cstructs(Nodes) ->
+ Filter = fun(Node) -> mnesia_monitor:needs_protocol_conversion(Node) end,
+ case lists:filter(Filter, Nodes) of
+ [] -> false;
+ Ns -> lists:min([element(1, ?catch_val({protocol, Node})) || Node <- Ns])
+ end.
tab_to_nodes(Tab) when is_atom(Tab) ->
Cs = val({Tab, cstruct}),
@@ -2952,8 +3649,8 @@ do_make_merge_schema(Node, NeedsConv, RemoteCs = #cstruct{}) ->
%% invariants must be enforced in order to allow merge of cstructs.
%%
%% Returns a new cstruct or issues a fatal error
-merge_cstructs(Cs, RemoteCs, Force) ->
- verify_cstruct(Cs),
+merge_cstructs(Cs0, RemoteCs, Force) ->
+ Cs = verify_cstruct(Cs0),
try do_merge_cstructs(Cs, RemoteCs, Force) of
MergedCs when is_record(MergedCs, cstruct) ->
MergedCs
@@ -2963,15 +3660,15 @@ merge_cstructs(Cs, RemoteCs, Force) ->
error:Reason -> exit(Reason)
end.
-do_merge_cstructs(Cs, RemoteCs, Force) ->
- verify_cstruct(RemoteCs),
+do_merge_cstructs(Cs, RemoteCs0, Force) ->
+ RemoteCs = verify_cstruct(RemoteCs0),
Ns = mnesia_lib:uniq(mnesia_lib:cs_to_nodes(Cs) ++
mnesia_lib:cs_to_nodes(RemoteCs)),
{AnythingNew, MergedCs} =
merge_storage_type(Ns, false, Cs, RemoteCs, Force),
- MergedCs2 = merge_versions(AnythingNew, MergedCs, RemoteCs, Force),
- verify_cstruct(MergedCs2),
- MergedCs2.
+ verify_cstruct(
+ merge_versions(AnythingNew, MergedCs, RemoteCs, Force)).
+
merge_storage_type([N | Ns], AnythingNew, Cs, RemoteCs, Force) ->
Local = mnesia_lib:cs_to_storage_type(N, Cs),
@@ -3115,4 +3812,3 @@ unannounce_im_running([N | Ns]) ->
unannounce_im_running(Ns);
unannounce_im_running([]) ->
ok.
-
diff --git a/lib/mnesia/src/mnesia_sup.erl b/lib/mnesia/src/mnesia_sup.erl
index 5037a17ada..4aece81308 100644
--- a/lib/mnesia/src/mnesia_sup.erl
+++ b/lib/mnesia/src/mnesia_sup.erl
@@ -60,9 +60,10 @@ init() ->
Flags = {one_for_all, 0, 3600}, % Should be rest_for_one policy
Event = event_procs(),
+ Ext = ext_procs(),
Kernel = kernel_procs(),
- {ok, {Flags, Event ++ Kernel}}.
+ {ok, {Flags, Event ++ Ext ++ Kernel}}.
event_procs() ->
KillAfter = timer:seconds(30),
@@ -75,6 +76,11 @@ kernel_procs() ->
KA = infinity,
[{K, {K, start, []}, permanent, KA, supervisor, [K, supervisor]}].
+ext_procs() ->
+ K = mnesia_ext_sup,
+ KA = infinity,
+ [{K, {K, start, []}, permanent, KA, supervisor, [K, supervisor]}].
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% event handler
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index 6888f61dbd..b116b48312 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -42,7 +42,8 @@
put_activity_id/2,
block_tab/1,
unblock_tab/1,
- fixtable/3
+ fixtable/3,
+ new_cr_format/1
]).
%% sys callback functions
@@ -206,10 +207,10 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=
{_From, {async_dirty, Tid, Commit, Tab}} ->
case lists:member(Tab, State#state.blocked_tabs) of
false ->
- do_async_dirty(Tid, Commit, Tab),
+ do_async_dirty(Tid, new_cr_format(Commit), Tab),
doit_loop(State);
true ->
- Item = {async_dirty, Tid, Commit, Tab},
+ Item = {async_dirty, Tid, new_cr_format(Commit), Tab},
State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]},
doit_loop(State2)
end;
@@ -217,10 +218,10 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=
{From, {sync_dirty, Tid, Commit, Tab}} ->
case lists:member(Tab, State#state.blocked_tabs) of
false ->
- do_sync_dirty(From, Tid, Commit, Tab),
+ do_sync_dirty(From, Tid, new_cr_format(Commit), Tab),
doit_loop(State);
true ->
- Item = {sync_dirty, From, Tid, Commit, Tab},
+ Item = {sync_dirty, From, Tid, new_cr_format(Commit), Tab},
State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]},
doit_loop(State2)
end;
@@ -241,10 +242,11 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=
reply(From, {error, {system_limit, Msg, Reason}}, State)
end;
- {From, {ask_commit, Protocol, Tid, Commit, DiscNs, RamNs}} ->
+ {From, {ask_commit, Protocol, Tid, Commit0, DiscNs, RamNs}} ->
?eval_debug_fun({?MODULE, doit_ask_commit},
[{tid, Tid}, {prot, Protocol}]),
mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),
+ Commit = new_cr_format(Commit0),
Pid =
case Protocol of
asym_trans when node(Tid#tid.pid) /= node() ->
@@ -1137,14 +1139,14 @@ arrange(Tid, Store, Type) ->
reverse([]) ->
[];
reverse([H=#commit{ram_copies=Ram, disc_copies=DC,
- disc_only_copies=DOC,snmp = Snmp}
+ disc_only_copies=DOC, ext=Ext}
|R]) ->
[
H#commit{
- ram_copies = lists:reverse(Ram),
- disc_copies = lists:reverse(DC),
- disc_only_copies = lists:reverse(DOC),
- snmp = lists:reverse(Snmp)
+ ram_copies = lists:reverse(Ram),
+ disc_copies = lists:reverse(DC),
+ disc_only_copies = lists:reverse(DOC),
+ ext = [{Type, lists:reverse(E)} || {Type,E} <- Ext]
}
| reverse(R)].
@@ -1311,8 +1313,13 @@ pick_node({dirty,_}, Node, [], Done) ->
pick_node(_Tid, Node, [], _Done) ->
mnesia:abort({bad_commit, {missing_lock, Node}}).
-prepare_node(Node, Storage, [Item | Items], Rec, Kind) when Kind == snmp ->
- Rec2 = Rec#commit{snmp = [Item | Rec#commit.snmp]},
+prepare_node(Node, Storage, [Item | Items], #commit{ext=Ext0}=Rec, Kind) when Kind == snmp ->
+ Rec2 = case lists:keytake(snmp, 1, Ext0) of
+ false ->
+ Rec#commit{ext = [{snmp,[Item]}|Ext0]};
+ {_, {snmp,Snmp},Ext} ->
+ Rec#commit{ext = [{snmp,[Item|Snmp]}|Ext]}
+ end,
prepare_node(Node, Storage, Items, Rec2, Kind);
prepare_node(Node, Storage, [Item | Items], Rec, Kind) when Kind /= schema ->
Rec2 =
@@ -1323,7 +1330,15 @@ prepare_node(Node, Storage, [Item | Items], Rec, Kind) when Kind /= schema ->
Rec#commit{disc_copies = [Item | Rec#commit.disc_copies]};
disc_only_copies ->
Rec#commit{disc_only_copies =
- [Item | Rec#commit.disc_only_copies]}
+ [Item | Rec#commit.disc_only_copies]};
+ {ext, Alias, Mod} ->
+ Ext0 = Rec#commit.ext,
+ case lists:keytake(ext_copies, 1, Ext0) of
+ false ->
+ Rec#commit{ext = [{ext_copies, [{{ext,Alias,Mod}, Item}]}|Ext0]};
+ {_,{_,EC},Ext} ->
+ Rec#commit{ext = [{ext_copies, [{{ext,Alias,Mod}, Item}|EC]}|Ext]}
+ end
end,
prepare_node(Node, Storage, Items, Rec2, Kind);
prepare_node(_Node, _Storage, Items, Rec, Kind)
@@ -1750,16 +1765,30 @@ do_commit(Tid, Bin) when is_binary(Bin) ->
do_commit(Tid, binary_to_term(Bin));
do_commit(Tid, C) ->
do_commit(Tid, C, optional).
+
do_commit(Tid, Bin, DumperMode) when is_binary(Bin) ->
do_commit(Tid, binary_to_term(Bin), DumperMode);
do_commit(Tid, C, DumperMode) ->
mnesia_dumper:update(Tid, C#commit.schema_ops, DumperMode),
- R = do_snmp(Tid, C#commit.snmp),
+ R = do_snmp(Tid, proplists:get_value(snmp, C#commit.ext, [])),
R2 = do_update(Tid, ram_copies, C#commit.ram_copies, R),
R3 = do_update(Tid, disc_copies, C#commit.disc_copies, R2),
R4 = do_update(Tid, disc_only_copies, C#commit.disc_only_copies, R3),
+ R5 = do_update_ext(Tid, C#commit.ext, R4),
mnesia_subscr:report_activity(Tid),
- R4.
+ R5.
+
+%% This could/should be optimized
+do_update_ext(_Tid, [], OldRes) -> OldRes;
+do_update_ext(Tid, Ext, OldRes) ->
+ case lists:keyfind(ext_copies, 1, Ext) of
+ false -> OldRes;
+ {_, Ops} ->
+ Do = fun({{ext, _,_} = Storage, Op}, R) ->
+ do_update(Tid, Storage, [Op], R)
+ end,
+ lists:foldl(Do, OldRes, Ops)
+ end.
%% Update the items
do_update(Tid, Storage, [Op | Ops], OldRes) ->
@@ -1782,12 +1811,12 @@ do_update(_Tid, _Storage, [], Res) ->
Res.
do_update_op(Tid, Storage, {{Tab, K}, Obj, write}) ->
- commit_write(?catch_val({Tab, commit_work}), Tid,
+ commit_write(?catch_val({Tab, commit_work}), Tid, Storage,
Tab, K, Obj, undefined),
mnesia_lib:db_put(Storage, Tab, Obj);
do_update_op(Tid, Storage, {{Tab, K}, Val, delete}) ->
- commit_delete(?catch_val({Tab, commit_work}), Tid, Tab, K, Val, undefined),
+ commit_delete(?catch_val({Tab, commit_work}), Tid, Storage, Tab, K, Val, undefined),
mnesia_lib:db_erase(Storage, Tab, K);
do_update_op(Tid, Storage, {{Tab, K}, {RecName, Incr}, update_counter}) ->
@@ -1805,86 +1834,84 @@ do_update_op(Tid, Storage, {{Tab, K}, {RecName, Incr}, update_counter}) ->
mnesia_lib:db_put(Storage, Tab, Zero),
{Zero, []}
end,
- commit_update(?catch_val({Tab, commit_work}), Tid, Tab,
+ commit_update(?catch_val({Tab, commit_work}), Tid, Storage, Tab,
K, NewObj, OldObjs),
element(3, NewObj);
do_update_op(Tid, Storage, {{Tab, Key}, Obj, delete_object}) ->
commit_del_object(?catch_val({Tab, commit_work}),
- Tid, Tab, Key, Obj, undefined),
+ Tid, Storage, Tab, Key, Obj),
mnesia_lib:db_match_erase(Storage, Tab, Obj);
do_update_op(Tid, Storage, {{Tab, Key}, Obj, clear_table}) ->
- commit_clear(?catch_val({Tab, commit_work}), Tid, Tab, Key, Obj),
+ commit_clear(?catch_val({Tab, commit_work}), Tid, Storage, Tab, Key, Obj),
mnesia_lib:db_match_erase(Storage, Tab, Obj).
-commit_write([], _, _, _, _, _) -> ok;
-commit_write([{checkpoints, CpList}|R], Tid, Tab, K, Obj, Old) ->
+commit_write([], _, _, _, _, _, _) -> ok;
+commit_write([{checkpoints, CpList}|R], Tid, Storage, Tab, K, Obj, Old) ->
mnesia_checkpoint:tm_retain(Tid, Tab, K, write, CpList),
- commit_write(R, Tid, Tab, K, Obj, Old);
-commit_write([H|R], Tid, Tab, K, Obj, Old)
+ commit_write(R, Tid, Storage, Tab, K, Obj, Old);
+commit_write([H|R], Tid, Storage, Tab, K, Obj, Old)
when element(1, H) == subscribers ->
mnesia_subscr:report_table_event(H, Tab, Tid, Obj, write, Old),
- commit_write(R, Tid, Tab, K, Obj, Old);
-commit_write([H|R], Tid, Tab, K, Obj, Old)
+ commit_write(R, Tid, Storage, Tab, K, Obj, Old);
+commit_write([H|R], Tid, Storage, Tab, K, Obj, Old)
when element(1, H) == index ->
- mnesia_index:add_index(H, Tab, K, Obj, Old),
- commit_write(R, Tid, Tab, K, Obj, Old).
+ mnesia_index:add_index(H, Storage, Tab, K, Obj, Old),
+ commit_write(R, Tid, Storage, Tab, K, Obj, Old).
-commit_update([], _, _, _, _, _) -> ok;
-commit_update([{checkpoints, CpList}|R], Tid, Tab, K, Obj, _) ->
+commit_update([], _, _, _, _, _, _) -> ok;
+commit_update([{checkpoints, CpList}|R], Tid, Storage, Tab, K, Obj, _) ->
Old = mnesia_checkpoint:tm_retain(Tid, Tab, K, write, CpList),
- commit_update(R, Tid, Tab, K, Obj, Old);
-commit_update([H|R], Tid, Tab, K, Obj, Old)
+ commit_update(R, Tid, Storage, Tab, K, Obj, Old);
+commit_update([H|R], Tid, Storage, Tab, K, Obj, Old)
when element(1, H) == subscribers ->
mnesia_subscr:report_table_event(H, Tab, Tid, Obj, write, Old),
- commit_update(R, Tid, Tab, K, Obj, Old);
-commit_update([H|R], Tid, Tab, K, Obj, Old)
+ commit_update(R, Tid, Storage, Tab, K, Obj, Old);
+commit_update([H|R], Tid,Storage, Tab, K, Obj, Old)
when element(1, H) == index ->
- mnesia_index:add_index(H, Tab, K, Obj, Old),
- commit_update(R, Tid, Tab, K, Obj, Old).
+ mnesia_index:add_index(H, Storage, Tab, K, Obj, Old),
+ commit_update(R, Tid, Storage, Tab, K, Obj, Old).
-commit_delete([], _, _, _, _, _) -> ok;
-commit_delete([{checkpoints, CpList}|R], Tid, Tab, K, Obj, _) ->
+commit_delete([], _, _, _, _, _, _) -> ok;
+commit_delete([{checkpoints, CpList}|R], Tid, Storage, Tab, K, Obj, _) ->
Old = mnesia_checkpoint:tm_retain(Tid, Tab, K, delete, CpList),
- commit_delete(R, Tid, Tab, K, Obj, Old);
-commit_delete([H|R], Tid, Tab, K, Obj, Old)
+ commit_delete(R, Tid, Storage, Tab, K, Obj, Old);
+commit_delete([H|R], Tid, Storage, Tab, K, Obj, Old)
when element(1, H) == subscribers ->
mnesia_subscr:report_table_event(H, Tab, Tid, Obj, delete, Old),
- commit_delete(R, Tid, Tab, K, Obj, Old);
-commit_delete([H|R], Tid, Tab, K, Obj, Old)
+ commit_delete(R, Tid, Storage, Tab, K, Obj, Old);
+commit_delete([H|R], Tid, Storage, Tab, K, Obj, Old)
when element(1, H) == index ->
- mnesia_index:delete_index(H, Tab, K),
- commit_delete(R, Tid, Tab, K, Obj, Old).
+ mnesia_index:delete_index(H, Storage, Tab, K),
+ commit_delete(R, Tid, Storage, Tab, K, Obj, Old).
commit_del_object([], _, _, _, _, _) -> ok;
-commit_del_object([{checkpoints, CpList}|R], Tid, Tab, K, Obj, _) ->
- Old = mnesia_checkpoint:tm_retain(Tid, Tab, K, delete_object, CpList),
- commit_del_object(R, Tid, Tab, K, Obj, Old);
-commit_del_object([H|R], Tid, Tab, K, Obj, Old)
- when element(1, H) == subscribers ->
- mnesia_subscr:report_table_event(H, Tab, Tid, Obj, delete_object, Old),
- commit_del_object(R, Tid, Tab, K, Obj, Old);
-commit_del_object([H|R], Tid, Tab, K, Obj, Old)
- when element(1, H) == index ->
- mnesia_index:del_object_index(H, Tab, K, Obj, Old),
- commit_del_object(R, Tid, Tab, K, Obj, Old).
-
-commit_clear([], _, _, _, _) -> ok;
-commit_clear([{checkpoints, CpList}|R], Tid, Tab, K, Obj) ->
+commit_del_object([{checkpoints, CpList}|R], Tid, Storage, Tab, K, Obj) ->
+ mnesia_checkpoint:tm_retain(Tid, Tab, K, delete_object, CpList),
+ commit_del_object(R, Tid, Storage, Tab, K, Obj);
+commit_del_object([H|R], Tid, Storage, Tab, K, Obj) when element(1, H) == subscribers ->
+ mnesia_subscr:report_table_event(H, Tab, Tid, Obj, delete_object),
+ commit_del_object(R, Tid, Storage, Tab, K, Obj);
+commit_del_object([H|R], Tid, Storage, Tab, K, Obj) when element(1, H) == index ->
+ mnesia_index:del_object_index(H, Storage, Tab, K, Obj),
+ commit_del_object(R, Tid, Storage, Tab, K, Obj).
+
+commit_clear([], _, _, _, _, _) -> ok;
+commit_clear([{checkpoints, CpList}|R], Tid, Storage, Tab, K, Obj) ->
mnesia_checkpoint:tm_retain(Tid, Tab, K, clear_table, CpList),
- commit_clear(R, Tid, Tab, K, Obj);
-commit_clear([H|R], Tid, Tab, K, Obj)
+ commit_clear(R, Tid, Storage, Tab, K, Obj);
+commit_clear([H|R], Tid, Storage, Tab, K, Obj)
when element(1, H) == subscribers ->
mnesia_subscr:report_table_event(H, Tab, Tid, Obj, clear_table, undefined),
- commit_clear(R, Tid, Tab, K, Obj);
-commit_clear([H|R], Tid, Tab, K, Obj)
+ commit_clear(R, Tid, Storage, Tab, K, Obj);
+commit_clear([H|R], Tid, Storage, Tab, K, Obj)
when element(1, H) == index ->
mnesia_index:clear_index(H, Tab, K, Obj),
- commit_clear(R, Tid, Tab, K, Obj).
+ commit_clear(R, Tid, Storage, Tab, K, Obj).
do_snmp(_, []) -> ok;
-do_snmp(Tid, [Head | Tail]) ->
+do_snmp(Tid, [Head|Tail]) ->
try mnesia_snmp_hook:update(Head)
catch _:Reason ->
%% This should only happen when we recently have
@@ -1896,31 +1923,34 @@ do_snmp(Tid, [Head | Tail]) ->
end,
do_snmp(Tid, Tail).
-commit_nodes([C | Tail], AccD, AccR)
- when C#commit.disc_copies == [],
- C#commit.disc_only_copies == [],
- C#commit.schema_ops == [] ->
- commit_nodes(Tail, AccD, [C#commit.node | AccR]);
commit_nodes([C | Tail], AccD, AccR) ->
- commit_nodes(Tail, [C#commit.node | AccD], AccR);
+ case C of
+ #commit{disc_copies=[], disc_only_copies=[], schema_ops=[], ext=Ext} ->
+ case lists:keyfind(ext_copies, 1, Ext) of
+ false -> commit_nodes(Tail, AccD, [C#commit.node | AccR]);
+ _ -> commit_nodes(Tail, [C#commit.node | AccD], AccR)
+ end;
+ _ ->
+ commit_nodes(Tail, [C#commit.node | AccD], AccR)
+ end;
commit_nodes([], AccD, AccR) ->
{AccD, AccR}.
commit_decision(D, [C | Tail], AccD, AccR) ->
N = C#commit.node,
{D2, Tail2} =
- case C#commit.schema_ops of
- [] when C#commit.disc_copies == [],
- C#commit.disc_only_copies == [] ->
- commit_decision(D, Tail, AccD, [N | AccR]);
- [] ->
+ case C of
+ #commit{disc_copies=[], disc_only_copies=[], schema_ops=[], ext=Ext} ->
+ case lists:keyfind(ext_copies, 1, Ext) of
+ false -> commit_decision(D, Tail, AccD, [N | AccR]);
+ _ -> commit_decision(D, Tail, [N | AccD], AccR)
+ end;
+ #commit{schema_ops=[]} ->
commit_decision(D, Tail, [N | AccD], AccR);
- Ops ->
+ #commit{schema_ops=Ops} ->
case ram_only_ops(N, Ops) of
- true ->
- commit_decision(D, Tail, AccD, [N | AccR]);
- false ->
- commit_decision(D, Tail, [N | AccD], AccR)
+ true -> commit_decision(D, Tail, AccD, [N | AccR]);
+ false -> commit_decision(D, Tail, [N | AccD], AccR)
end
end,
{D2, [C#commit{decision = D2} | Tail2]};
@@ -1948,7 +1978,7 @@ sync_send_dirty(Tid, [Head | Tail], Tab, WaitFor) ->
Res = do_dirty(Tid, Head),
{WF, Res};
true ->
- {?MODULE, Node} ! {self(), {sync_dirty, Tid, Head, Tab}},
+ {?MODULE, Node} ! {self(), {sync_dirty, Tid, ext_format(Head), Tab}},
sync_send_dirty(Tid, Tail, Tab, [Node | WaitFor])
end;
sync_send_dirty(_Tid, [], _Tab, WaitFor) ->
@@ -1967,11 +1997,11 @@ async_send_dirty(Tid, [Head | Tail], Tab, ReadNode, WaitFor, Res) ->
NewRes = do_dirty(Tid, Head),
async_send_dirty(Tid, Tail, Tab, ReadNode, WaitFor, NewRes);
ReadNode == Node ->
- {?MODULE, Node} ! {self(), {sync_dirty, Tid, Head, Tab}},
+ {?MODULE, Node} ! {self(), {sync_dirty, Tid, ext_format(Head), Tab}},
NewRes = {'EXIT', {aborted, {node_not_running, Node}}},
async_send_dirty(Tid, Tail, Tab, ReadNode, [Node | WaitFor], NewRes);
true ->
- {?MODULE, Node} ! {self(), {async_dirty, Tid, Head, Tab}},
+ {?MODULE, Node} ! {self(), {async_dirty, Tid, ext_format(Head), Tab}},
async_send_dirty(Tid, Tail, Tab, ReadNode, WaitFor, Res)
end;
async_send_dirty(_Tid, [], _Tab, _ReadNode, WaitFor, Res) ->
@@ -2028,23 +2058,29 @@ ask_commit(Protocol, Tid, [Head | Tail], DiscNs, RamNs, WaitFor, Local) ->
Node == node() ->
ask_commit(Protocol, Tid, Tail, DiscNs, RamNs, WaitFor, Head);
true ->
- Bin = opt_term_to_binary(Protocol, Head, DiscNs++RamNs),
- Msg = {ask_commit, Protocol, Tid, Bin, DiscNs, RamNs},
+ CR = ext_format(Head),
+ Msg = {ask_commit, Protocol, Tid, CR, DiscNs, RamNs},
{?MODULE, Node} ! {self(), Msg},
ask_commit(Protocol, Tid, Tail, DiscNs, RamNs, [Node | WaitFor], Local)
end;
ask_commit(_Protocol, _Tid, [], _DiscNs, _RamNs, WaitFor, Local) ->
{WaitFor, Local}.
-%% This used to test protocol conversion between mnesia-nodes
-%% but it is really dependent on the emulator version on the
-%% two nodes (if funs are sent which they are in transform table op).
-%% to be safe we let erts do the translation (many times maybe and thus
-%% slower but it works.
-% opt_term_to_binary(asym_trans, Head, Nodes) ->
-% opt_term_to_binary(Nodes, Head);
-opt_term_to_binary(_Protocol, Head, _Nodes) ->
- Head.
+ext_format(#commit{ext=[]}=CR) -> CR;
+ext_format(#commit{node=Node, ext=Ext}=CR) ->
+ case mnesia_monitor:needs_protocol_conversion(Node) of
+ true ->
+ case lists:keyfind(snmp, 1, Ext) of
+ false -> CR#commit{ext=[]};
+ {snmp, List} -> CR#commit{ext=List}
+ end;
+ false -> CR
+ end.
+
+new_cr_format(#commit{ext=[]}=Cr) -> Cr;
+new_cr_format(#commit{ext=[{_,_}|_]}=Cr) -> Cr;
+new_cr_format(#commit{ext=Snmp}=Cr) ->
+ Cr#commit{ext=[{snmp,Snmp}]}.
rec_all([Node | Tail], Tid, Res, Pids) ->
receive
diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile
index f0efbe6375..5b61b1af65 100644
--- a/lib/mnesia/test/Makefile
+++ b/lib/mnesia/test/Makefile
@@ -52,7 +52,8 @@ MODULES= \
mnesia_schema_recovery_test \
mnesia_measure_test \
mnesia_cost \
- mnesia_dbn_meters
+ mnesia_dbn_meters \
+ ext_test
DocExamplesDir := ../doc/src/
diff --git a/lib/mnesia/test/ext_test.erl b/lib/mnesia/test/ext_test.erl
new file mode 100644
index 0000000000..45ddb148bc
--- /dev/null
+++ b/lib/mnesia/test/ext_test.erl
@@ -0,0 +1,237 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ext_test).
+
+%% Initializations
+-export([init_backend/0, add_aliases/1, remove_aliases/1,
+ check_definition/4, semantics/2]).
+
+-export([
+ create_table/3, load_table/4,
+ delete_table/2, close_table/2, sync_close_table/2,
+
+ sender_init/4,
+ receiver_first_message/4, receive_data/5, receive_done/4,
+
+ index_is_consistent/3, is_index_consistent/2,
+
+ real_suffixes/0, tmp_suffixes/0,
+
+ info/3,
+ fixtable/3,
+ validate_key/6, validate_record/6,
+
+ first/2, last/2, next/3, prev/3, slot/3,
+
+ insert/3, update_counter/4,
+ lookup/3,
+ delete/3, match_delete/3,
+ select/1, select/3, select/4, repair_continuation/2
+ ]).
+
+-ifdef(DEBUG).
+-define(DBG(DATA), io:format("~p:~p: ~p~n",[?MODULE, ?LINE, DATA])).
+-define(DBG(FORMAT, ARGS), io:format("~p:~p: " ++ FORMAT,[?MODULE, ?LINE] ++ ARGS)).
+-else.
+-define(DBG(DATA), ok).
+-define(DBG(FORMAT, ARGS), ok).
+-endif.
+
+%% types() ->
+%% [{fs_copies, ?MODULE},
+%% {raw_fs_copies, ?MODULE}].
+
+semantics(ext_ets, storage) -> ram_copies;
+semantics(ext_ets, types ) -> [set, ordered_set, bag];
+semantics(ext_ets, index_types) -> [ordered];
+semantics(_Alias, _) ->
+ undefined.
+
+%% valid_op(_, _) ->
+%% true.
+
+init_backend() ->
+ ?DBG(init_backend),
+ ok.
+
+add_aliases(_As) ->
+ ?DBG(_As),
+ ok.
+
+remove_aliases(_) ->
+ ok.
+
+
+%% Table operations
+
+check_definition(ext_ets, _Tab, _Nodes, _Props) ->
+ ?DBG("~p ~p ~p~n", [_Tab, _Nodes, _Props]),
+ ok.
+
+create_table(ext_ets, Tab, Props) when is_atom(Tab) ->
+ Tid = ets:new(Tab, [public, proplists:get_value(type, Props, set), {keypos, 2}]),
+ ?DBG("~p Create: ~p(~p) ~p~n", [self(), Tab, Tid, Props]),
+ mnesia_lib:set({?MODULE, Tab}, Tid),
+ ok;
+create_table(_, Tag={Tab, index, {_Where, Type0}}, _Opts) ->
+ Type = case Type0 of
+ ordered -> ordered_set;
+ _ -> Type0
+ end,
+ Tid = ets:new(Tab, [public, Type]),
+ ?DBG("~p(~p) ~p~n", [Tab, Tid, Tag]),
+ mnesia_lib:set({?MODULE, Tag}, Tid),
+ ok;
+create_table(_, Tag={_Tab, retainer, ChkPName}, _Opts) ->
+ Tid = ets:new(ChkPName, [set, public, {keypos, 2}]),
+ ?DBG("~p(~p) ~p~n", [_Tab, Tid, Tag]),
+ mnesia_lib:set({?MODULE, Tag}, Tid),
+ ok.
+
+delete_table(ext_ets, Tab) ->
+ try
+ ets:delete(mnesia_lib:val({?MODULE,Tab})),
+ mnesia_lib:unset({?MODULE,Tab}),
+ ok
+ catch _:_ ->
+ ?DBG({double_delete, Tab}),
+ ok
+ end.
+
+load_table(ext_ets, _Tab, init_index, _Cs) -> ok;
+load_table(ext_ets, _Tab, _LoadReason, _Cs) ->
+ ?DBG("Load ~p ~p~n", [_Tab, _LoadReason]),
+ ok.
+%% mnesia_monitor:unsafe_create_external(Tab, ext_ets, ?MODULE, Cs).
+
+sender_init(Alias, Tab, _RemoteStorage, _Pid) ->
+ KeysPerTransfer = 100,
+ {standard,
+ fun() -> mnesia_lib:db_init_chunk({ext,Alias,?MODULE}, Tab, KeysPerTransfer) end,
+ fun(Cont) -> mnesia_lib:db_chunk({ext,Alias,?MODULE}, Cont) end}.
+
+receiver_first_message(Sender, {first, Size}, _Alias, Tab) ->
+ ?DBG({first,Size}),
+ {Size, {Tab, Sender}}.
+
+receive_data(Data, ext_ets, Name, _Sender, {Name, Tab, _Sender}=State) ->
+ ?DBG({Data,State}),
+ true = ets:insert(Tab, Data),
+ {more, State};
+receive_data(Data, Alias, Tab, Sender, {Name, Sender}) ->
+ receive_data(Data, Alias, Tab, Sender, {Name, mnesia_lib:val({?MODULE,Tab}), Sender}).
+
+receive_done(_Alias, _Tab, _Sender, _State) ->
+ ?DBG({done,_State}),
+ ok.
+
+close_table(Alias, Tab) -> sync_close_table(Alias, Tab).
+
+sync_close_table(ext_ets, _Tab) ->
+ ?DBG(_Tab).
+
+fixtable(ext_ets, Tab, Bool) ->
+ ?DBG({Tab,Bool}),
+ ets:safe_fixtable(mnesia_lib:val({?MODULE,Tab}), Bool).
+
+info(ext_ets, Tab, Type) ->
+ ?DBG({Tab,Type}),
+ Tid = mnesia_lib:val({?MODULE,Tab}),
+ try ets:info(Tid, Type) of
+ Val -> Val
+ catch _:_ ->
+ undefined
+ end.
+
+real_suffixes() ->
+ [".dat"].
+
+tmp_suffixes() ->
+ [].
+
+%% Index
+
+index_is_consistent(_Alias, _Ix, _Bool) -> ok. % Ignore for now
+is_index_consistent(_Alias, _Ix) -> false. % Always rebuild
+
+%% Record operations
+
+validate_record(_Alias, _Tab, RecName, Arity, Type, _Obj) ->
+ {RecName, Arity, Type}.
+
+validate_key(_Alias, _Tab, RecName, Arity, Type, _Key) ->
+ {RecName, Arity, Type}.
+
+insert(ext_ets, Tab, Obj) ->
+ ?DBG({Tab,Obj}),
+ try
+ ets:insert(mnesia_lib:val({?MODULE,Tab}), Obj),
+ ok
+ catch _:Reason ->
+ io:format("CRASH ~p ~p~n",[Reason, mnesia_lib:val({?MODULE,Tab})])
+ end.
+
+lookup(ext_ets, Tab, Key) ->
+ ets:lookup(mnesia_lib:val({?MODULE,Tab}), Key).
+
+delete(ext_ets, Tab, Key) ->
+ ets:delete(mnesia_lib:val({?MODULE,Tab}), Key).
+
+match_delete(ext_ets, Tab, Pat) ->
+ ets:match_delete(mnesia_lib:val({?MODULE,Tab}), Pat).
+
+first(ext_ets, Tab) ->
+ ets:first(mnesia_lib:val({?MODULE,Tab})).
+
+last(Alias, Tab) -> first(Alias, Tab).
+
+next(ext_ets, Tab, Key) ->
+ ets:next(mnesia_lib:val({?MODULE,Tab}), Key).
+
+prev(Alias, Tab, Key) ->
+ next(Alias, Tab, Key).
+
+slot(ext_ets, Tab, Pos) ->
+ ets:slot(mnesia_lib:val({?MODULE,Tab}), Pos).
+
+update_counter(ext_ets, Tab, C, Val) ->
+ ets:update_counter(mnesia_lib:val({?MODULE,Tab}), C, Val).
+
+select('$end_of_table' = End) -> End;
+select({ext_ets, C}) -> ets:select(C).
+
+select(Alias, Tab, Ms) ->
+ Res = select(Alias, Tab, Ms, 100000),
+ select_1(Res).
+
+select_1('$end_of_table') -> [];
+select_1({Acc, C}) ->
+ case ets:select(C) of
+ '$end_of_table' -> Acc;
+ {New, Cont} ->
+ select_1({New ++ Acc, Cont})
+ end.
+
+select(ext_ets, Tab, Ms, Limit) when is_integer(Limit); Limit =:= infinity ->
+ ets:select(mnesia_lib:val({?MODULE,Tab}), Ms, Limit).
+
+repair_continuation({Alias, Cont}, Ms) ->
+ {Alias, ets:repair_continuation(Cont, Ms)}.
diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl
index a31c9b1f4f..45da909264 100644
--- a/lib/mnesia/test/mnesia_config_test.erl
+++ b/lib/mnesia/test/mnesia_config_test.erl
@@ -693,9 +693,9 @@ event_module(Config) when is_list(Config) ->
end,
?match({[ok, ok], []}, rpc:multicall(Nodes, mnesia, start, [Def])),
- receive after 1000 -> ok end,
+ receive after 2000 -> ok end,
mnesia_event ! {get_log, self()},
- DebugLog1 = receive
+ DebugLog1 = receive
{log, L1} -> L1
after 10000 -> [timeout]
end,
@@ -706,9 +706,9 @@ event_module(Config) when is_list(Config) ->
?match({[ok], []}, rpc:multicall([N2], mnesia, start, [])),
- receive after 1000 -> ok end,
+ receive after 2000 -> ok end,
mnesia_event ! {get_log, self()},
- DebugLog = receive
+ DebugLog = receive
{log, L} -> L
after 10000 -> [timeout]
end,
@@ -1100,8 +1100,8 @@ dynamic_basic(Config) when is_list(Config) ->
?match(ok, mnesia:dirty_write({tab1, 1, 1})),
?match(ok, mnesia:dirty_write({tab2, 1, 1})),
-
- ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes, [N1]}]])),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes, [N1]}, {schema, ?BACKEND}]])),
?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab1,tab2],5000])),
io:format("Here ~p ~n",[?LINE]),
check_storage(N2, N1, [N3]),
@@ -1112,7 +1112,7 @@ dynamic_basic(Config) when is_list(Config) ->
?match(ok, mnesia:delete_schema([N3])),
io:format("T1 ~p ~n",[rpc:call(N3,?MODULE,c_nodes,[])]),
- ?match(ok, rpc:call(N3, mnesia, start, [])),
+ ?match(ok, rpc:call(N3, mnesia, start, [[{schema, ?BACKEND}]])),
io:format("T2 ~p ~n",[rpc:call(N3,?MODULE,c_nodes,[])]),
timer:sleep(2000),
io:format("T3 ~p ~n",[rpc:call(N3,?MODULE,c_nodes,[])]),
@@ -1127,7 +1127,7 @@ dynamic_basic(Config) when is_list(Config) ->
?match([], mnesia_test_lib:kill_mnesia([N3])),
?match(ok, mnesia:delete_schema([N3])),
- ?match(ok, rpc:call(N3, mnesia, start, [])),
+ ?match(ok, rpc:call(N3, mnesia, start, [[{schema, ?BACKEND}]])),
?match({ok, [N3]}, sort(?rpc_connect(N1, [N3]))),
?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[tab1,tab2],5000])),
io:format("Here ~p ~n",[?LINE]),
@@ -1143,7 +1143,7 @@ dynamic_basic(Config) when is_list(Config) ->
% mnesia should come up now.
?match({atomic, ok}, mnesia:add_table_copy(tab1, N2, ram_copies)),
- ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema, ?BACKEND}]])),
?match({ok, _}, sort(?rpc_connect(N2, [N3]))),
?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
@@ -1162,7 +1162,7 @@ dynamic_basic(Config) when is_list(Config) ->
?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]))),
- ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema, ?BACKEND}]])),
?match({ok, _}, sort(?rpc_connect(N3, [N2]))),
?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
@@ -1192,7 +1192,7 @@ dynamic_ext(Config) when is_list(Config) ->
mnesia_test_lib:kill_mnesia([N2]),
?match(ok, mnesia:delete_schema([N2])),
- ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes, [N1]}]])),
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes, [N1]}, {schema, ?BACKEND}]])),
?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
@@ -1213,7 +1213,7 @@ dynamic_ext(Config) when is_list(Config) ->
?match(ok, mnesia:dirty_write({tab3, 42, T})),
?match(stopped, rpc:call(N2, mnesia, stop, [])),
- ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema, ?BACKEND}]])),
?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
?match(ok, mnesia:wait_for_tables([tab0,tab1,tab2,tab3], 10000)),
?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab1,tab2,tab3], 100])),
@@ -1226,7 +1226,7 @@ dynamic_ext(Config) when is_list(Config) ->
?match(stopped, rpc:call(N1, mnesia, stop, [])),
- ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1,N2]}]])),
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1,N2]}, {schema, ?BACKEND}]])),
?match({timeout,[tab0]}, rpc:call(N2, mnesia, wait_for_tables, [[tab0], 500])),
?match(ok, rpc:call(N1, mnesia, start, [[{extra_db_nodes, [N1,N2]}]])),
@@ -1238,7 +1238,7 @@ dynamic_ext(Config) when is_list(Config) ->
?match(stopped, rpc:call(N1, mnesia, stop, [])),
mnesia_test_lib:kill_mnesia([N2]),
?match(ok, mnesia:delete_schema([N2])),
- ?match(ok, rpc:call(N1, mnesia, start, [[{extra_db_nodes, [N1,N2]}]])),
+ ?match(ok, rpc:call(N1, mnesia, start, [[{extra_db_nodes, [N1,N2]}, {schema, ?BACKEND}]])),
?match({timeout,[tab0]}, rpc:call(N1, mnesia, wait_for_tables, [[tab0], 500])),
?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1,N2]}]])),
diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl
index 6c3e68ba38..9cc84de87b 100644
--- a/lib/mnesia/test/mnesia_consistency_test.erl
+++ b/lib/mnesia/test/mnesia_consistency_test.erl
@@ -565,6 +565,7 @@ consistency_after_fallback_3_disc_only(Config) when is_list(Config) ->
consistency_after_fallback(disc_only_copies, 3, Config).
consistency_after_fallback(ReplicaType, NodeConfig, Config) ->
+ put(mnesia_test_verbose, true),
%%?verbose("Starting consistency_after_fallback2 at ~p~n", [self()]),
Delay = 5,
Nodes = ?acquire_nodes(NodeConfig, [{tc_timeout, timer:minutes(10)} | Config]),
@@ -594,10 +595,11 @@ consistency_after_fallback(ReplicaType, NodeConfig, Config) ->
?match(ok, mnesia_tpcb:verify_tabs()),
%% Stop and then start mnesia and check table consistency
- %%?verbose("Restarting Mnesia~n", []),
+ ?verbose("Kill Mnesia~n", []),
mnesia_test_lib:kill_mnesia(Nodes),
+ ?verbose("Start Mnesia~n", []),
mnesia_test_lib:start_mnesia(Nodes,[account,branch,teller,history]),
-
+ ?verbose("Verify tabs~n", []),
?match(ok, mnesia_tpcb:verify_tabs()),
if
ReplicaType == ram_copies ->
diff --git a/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl
index 88b3a215e1..c9df8ed353 100644
--- a/lib/mnesia/test/mnesia_dirty_access_test.erl
+++ b/lib/mnesia/test/mnesia_dirty_access_test.erl
@@ -40,43 +40,40 @@ all() ->
groups() ->
[{dirty_write, [],
- [dirty_write_ram, dirty_write_disc,
- dirty_write_disc_only]},
+ [dirty_write_ram, dirty_write_disc, dirty_write_disc_only,
+ dirty_write_xets]},
{dirty_read, [],
- [dirty_read_ram, dirty_read_disc,
- dirty_read_disc_only]},
+ [dirty_read_ram, dirty_read_disc, dirty_read_disc_only, dirty_read_xets]},
{dirty_update_counter, [],
[dirty_update_counter_ram, dirty_update_counter_disc,
- dirty_update_counter_disc_only]},
+ dirty_update_counter_disc_only, dirty_update_counter_xets]},
{dirty_delete, [],
[dirty_delete_ram, dirty_delete_disc,
- dirty_delete_disc_only]},
+ dirty_delete_disc_only, dirty_delete_xets]},
{dirty_delete_object, [],
[dirty_delete_object_ram, dirty_delete_object_disc,
- dirty_delete_object_disc_only]},
+ dirty_delete_object_disc_only, dirty_delete_object_xets]},
{dirty_match_object, [],
[dirty_match_object_ram, dirty_match_object_disc,
- dirty_match_object_disc_only]},
+ dirty_match_object_disc_only, dirty_match_object_xets]},
{dirty_index, [],
[{group, dirty_index_match_object},
{group, dirty_index_read},
{group, dirty_index_update}]},
{dirty_index_match_object, [],
- [dirty_index_match_object_ram,
- dirty_index_match_object_disc,
- dirty_index_match_object_disc_only]},
+ [dirty_index_match_object_ram, dirty_index_match_object_disc,
+ dirty_index_match_object_disc_only, dirty_index_match_object_xets]},
{dirty_index_read, [],
[dirty_index_read_ram, dirty_index_read_disc,
- dirty_index_read_disc_only]},
+ dirty_index_read_disc_only, dirty_index_read_xets]},
{dirty_index_update, [],
- [dirty_index_update_set_ram,
- dirty_index_update_set_disc,
- dirty_index_update_set_disc_only,
+ [dirty_index_update_set_ram, dirty_index_update_set_disc,
+ dirty_index_update_set_disc_only, dirty_index_update_set_xets,
dirty_index_update_bag_ram, dirty_index_update_bag_disc,
- dirty_index_update_bag_disc_only]},
+ dirty_index_update_bag_disc_only, dirty_index_update_bag_xets]},
{dirty_iter, [],
- [dirty_iter_ram, dirty_iter_disc,
- dirty_iter_disc_only]},
+ [dirty_iter_ram, dirty_iter_disc, dirty_iter_disc_only,
+ dirty_iter_xets]},
{admin_tests, [],
[del_table_copy_1, del_table_copy_2, del_table_copy_3,
add_table_copy_1, add_table_copy_2, add_table_copy_3,
@@ -106,6 +103,9 @@ dirty_write_disc_only(suite) -> [];
dirty_write_disc_only(Config) when is_list(Config) ->
dirty_write(Config, disc_only_copies).
+dirty_write_xets(Config) when is_list(Config) ->
+ dirty_write(Config, ext_ets).
+
dirty_write(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_write,
@@ -137,6 +137,9 @@ dirty_read_disc_only(suite) -> [];
dirty_read_disc_only(Config) when is_list(Config) ->
dirty_read(Config, disc_only_copies).
+dirty_read_xets(Config) when is_list(Config) ->
+ dirty_read(Config, ext_ets).
+
dirty_read(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_read,
@@ -180,6 +183,9 @@ dirty_update_counter_disc_only(suite) -> [];
dirty_update_counter_disc_only(Config) when is_list(Config) ->
dirty_update_counter(Config, disc_only_copies).
+dirty_update_counter_xets(Config) when is_list(Config) ->
+ dirty_update_counter(Config, ext_ets).
+
dirty_update_counter(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_update_counter,
@@ -222,6 +228,9 @@ dirty_delete_disc_only(suite) -> [];
dirty_delete_disc_only(Config) when is_list(Config) ->
dirty_delete(Config, disc_only_copies).
+dirty_delete_xets(Config) when is_list(Config) ->
+ dirty_delete(Config, ext_ets).
+
dirty_delete(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_delete,
@@ -259,6 +268,9 @@ dirty_delete_object_disc_only(suite) -> [];
dirty_delete_object_disc_only(Config) when is_list(Config) ->
dirty_delete_object(Config, disc_only_copies).
+dirty_delete_object_xets(Config) when is_list(Config) ->
+ dirty_delete_object(Config, ext_ets).
+
dirty_delete_object(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_delete_object,
@@ -302,6 +314,9 @@ dirty_match_object_disc_only(suite) -> [];
dirty_match_object_disc_only(Config) when is_list(Config) ->
dirty_match_object(Config, disc_only_copies).
+dirty_match_object_xets(Config) when is_list(Config) ->
+ dirty_match_object(Config, ext_ets).
+
dirty_match_object(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_match,
@@ -326,7 +341,6 @@ dirty_match_object(Config, Storage) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Dirty read matching records by using an index
-
dirty_index_match_object_ram(suite) -> [];
dirty_index_match_object_ram(Config) when is_list(Config) ->
dirty_index_match_object(Config, ram_copies).
@@ -339,6 +353,9 @@ dirty_index_match_object_disc_only(suite) -> [];
dirty_index_match_object_disc_only(Config) when is_list(Config) ->
dirty_index_match_object(Config, disc_only_copies).
+dirty_index_match_object_xets(Config) when is_list(Config) ->
+ dirty_index_match_object(Config, ext_ets).
+
dirty_index_match_object(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_index_match_object,
@@ -376,6 +393,9 @@ dirty_index_read_disc_only(suite) -> [];
dirty_index_read_disc_only(Config) when is_list(Config) ->
dirty_index_read(Config, disc_only_copies).
+dirty_index_read_xets(Config) when is_list(Config) ->
+ dirty_index_read(Config, ext_ets).
+
dirty_index_read(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_index_read,
@@ -437,6 +457,9 @@ dirty_index_update_set_disc_only(suite) -> [];
dirty_index_update_set_disc_only(Config) when is_list(Config) ->
dirty_index_update_set(Config, disc_only_copies).
+dirty_index_update_set_xets(Config) when is_list(Config) ->
+ dirty_index_update_set(Config, ext_ets).
+
dirty_index_update_set(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = index_test,
@@ -525,6 +548,9 @@ dirty_index_update_bag_disc_only(suite) -> [];
dirty_index_update_bag_disc_only(Config)when is_list(Config) ->
dirty_index_update_bag(Config, disc_only_copies).
+dirty_index_update_bag_xets(Config) when is_list(Config) ->
+ dirty_index_update_bag(Config, ext_ets).
+
dirty_index_update_bag(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = index_test,
@@ -631,7 +657,6 @@ dirty_index_update_bag(Config, Storage) ->
%% Dirty iteration
%% dirty_slot, dirty_first, dirty_next
-
dirty_iter_ram(suite) -> [];
dirty_iter_ram(Config) when is_list(Config) ->
dirty_iter(Config, ram_copies).
@@ -644,6 +669,9 @@ dirty_iter_disc_only(suite) -> [];
dirty_iter_disc_only(Config) when is_list(Config) ->
dirty_iter(Config, disc_only_copies).
+dirty_iter_xets(Config) when is_list(Config) ->
+ dirty_iter(Config, ext_ets).
+
dirty_iter(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_iter,
diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl
index 2ed62b1538..6e34040bc4 100644
--- a/lib/mnesia/test/mnesia_evil_coverage_test.erl
+++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl
@@ -72,7 +72,9 @@ groups() ->
{record_name_dirty_access, [],
[record_name_dirty_access_ram,
record_name_dirty_access_disc,
- record_name_dirty_access_disc_only]}].
+ record_name_dirty_access_disc_only,
+ record_name_dirty_access_xets
+ ]}].
init_per_group(_GroupName, Config) ->
Config.
@@ -112,6 +114,7 @@ system_info(Config) when is_list(Config) ->
?match(I when is_integer(I), mnesia:system_info(transaction_log_writes)),
?match(I when is_integer(I), mnesia:system_info(send_compressed)),
?match(L when is_list(L), mnesia:system_info(all)),
+ ?match(L when is_list(L), mnesia:system_info(backend_types)),
?match({'EXIT', {aborted, Reason }} when element(1, Reason) == badarg
, mnesia:system_info(ali_baba)),
?verify_mnesia(Nodes, []).
@@ -132,11 +135,11 @@ table_info(Config) when is_list(Config) ->
Schema =
case mnesia_test_lib:diskless(Config) of
true -> [{type, Type}, {attributes, Attrs}, {index, [ValPos]},
- {ram_copies, Nodes}];
+ {ram_copies, [Node1, Node2]}, {ext_ets, [Node3]}];
false ->
[{type, Type}, {attributes, Attrs}, {index, [ValPos]},
- {disc_only_copies, [Node1]}, {ram_copies, [Node2]},
- {disc_copies, [Node3]}]
+ {disc_only_copies, [Node1]}, {ram_copies, [Node2]},
+ {ext_ets, [Node3]}]
end,
?match({atomic, ok}, mnesia:create_table(Tab, Schema)),
@@ -144,28 +147,22 @@ table_info(Config) when is_list(Config) ->
Keys = lists:seq(1, Size),
Records = [{Tab, A, 7} || A <- Keys],
lists:foreach(fun(Rec) -> ?match(ok, mnesia:dirty_write(Rec)) end, Records),
- ?match(Mem when is_integer(Mem), mnesia:table_info(Tab, memory)),
- ?match(Size, mnesia:table_info(Tab, size)),
- ?match(Type, mnesia:table_info(Tab, type)),
case mnesia_test_lib:diskless(Config) of
true ->
?match(Nodes, mnesia:table_info(Tab, ram_copies));
false ->
- ?match([Node3], mnesia:table_info(Tab, mnesia_test_lib:storage_type(disc_copies, Config))),
+ ?match([Node3], mnesia:table_info(Tab, ext_ets)),
?match([Node2], mnesia:table_info(Tab, ram_copies)),
?match([Node1], mnesia:table_info(Tab, mnesia_test_lib:storage_type(disc_only_copies, Config)))
end,
Read = [Node1, Node2, Node3],
- ?match(true, lists:member(mnesia:table_info(Tab, where_to_read), Read)),
Write = ?sort([Node1, Node2, Node3]),
- ?match(Write, ?sort(mnesia:table_info(Tab, where_to_write))),
- ?match([ValPos], mnesia:table_info(Tab, index)),
- ?match(Arity, mnesia:table_info(Tab, arity)),
- ?match(Attrs, mnesia:table_info(Tab, attributes)),
- ?match({Tab, '_', '_'}, mnesia:table_info(Tab, wild_pattern)),
- ?match({atomic, Attrs}, mnesia:transaction(fun() ->
- mnesia:table_info(Tab, attributes) end)),
+
+ {[ok,ok,ok], []} = rpc:multicall(Nodes, ?MODULE, info_check,
+ [Tab, Read, Write, Size, Type, ValPos, Arity, Attrs]),
+
+ ?match({atomic, Attrs}, mnesia:transaction(fun() -> mnesia:table_info(Tab, attributes) end)),
?match(L when is_list(L), mnesia:table_info(Tab, all)),
@@ -179,6 +176,17 @@ table_info(Config) when is_list(Config) ->
?match(0, mnesia:table_info(tab_info, size)),
?verify_mnesia([Node1, Node3], [Node2]).
+info_check(Tab, Read, Write, Size, Type, ValPos, Arity, Attrs) ->
+ ?match(true, lists:member(mnesia:table_info(Tab, where_to_read), Read)),
+ ?match(Write, ?sort(mnesia:table_info(Tab, where_to_write))),
+ ?match(Mem when is_integer(Mem), mnesia:table_info(Tab, memory)),
+ ?match(Size, mnesia:table_info(Tab, size)),
+ ?match(Type, mnesia:table_info(Tab, type)),
+ ?match([ValPos], mnesia:table_info(Tab, index)),
+ ?match(Arity, mnesia:table_info(Tab, arity)),
+ ?match(Attrs, mnesia:table_info(Tab, attributes)),
+ ?match({Tab, '_', '_'}, mnesia:table_info(Tab, wild_pattern)),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Check the error descriptions
@@ -216,11 +224,12 @@ db_node_lifecycle(Config) when is_list(Config) ->
[Node1, Node2, Node3] = AllNodes = ?acquire_nodes(3, Config),
Tab = db_node_lifecycle,
- Who = fun(T) ->
+ Who = fun(T) ->
L1 = mnesia:table_info(T, ram_copies),
L2 = mnesia:table_info(T, disc_copies),
L3 = mnesia:table_info(T, disc_only_copies),
- L1 ++ L2 ++ L3
+ L4 = mnesia:table_info(T, ext_ets),
+ L1 ++ L2 ++ L3 ++ L4
end,
SNs = ?sort(AllNodes),
@@ -235,6 +244,7 @@ db_node_lifecycle(Config) when is_list(Config) ->
?match({error, _}, mnesia:create_schema([foo@bar])),
?match(ok, mnesia:start()),
?match(false, mnesia:system_info(use_dir)),
+ ?match([ram_copies, disc_copies, disc_only_copies], mnesia:system_info(backend_types)),
?match({atomic, ok}, mnesia:create_table(Tab, [])),
?match({aborted, {has_no_disc, Node1}}, mnesia:dump_tables([Tab])),
?match({aborted, {has_no_disc, Node1}}, mnesia:change_table_copy_type(Tab, node(), disc_copies)),
@@ -242,7 +252,7 @@ db_node_lifecycle(Config) when is_list(Config) ->
?match(stopped, mnesia:stop()),
- ?match(ok, mnesia:create_schema(AllNodes)),
+ ?match(ok, mnesia:create_schema(AllNodes, ?BACKEND)),
?match([], mnesia_test_lib:start_mnesia(AllNodes)),
?match([SNs, SNs, SNs],
@@ -271,12 +281,15 @@ db_node_lifecycle(Config) when is_list(Config) ->
Tab3 = not_local,
Tab4 = local,
Tab5 = remote,
+ Tab6 = ext1,
Tabs = [Schema,
[{name, Tab2}, {disc_copies, AllNodes}],
[{name, Tab3}, {ram_copies, [Node2, Node3]}],
[{name, Tab4}, {disc_only_copies, [Node1]}],
- [{name, Tab5}, {disc_only_copies, [Node2]}]],
+ [{name, Tab5}, {disc_only_copies, [Node2]}],
+ [{name, Tab6}, {ext_ets, [Node1, Node2]}]
+ ],
[?match({atomic, ok}, mnesia:create_table(T)) || T <- Tabs ],
@@ -287,31 +300,28 @@ db_node_lifecycle(Config) when is_list(Config) ->
?match({aborted, {node_not_running, Node1}},
mnesia:del_table_copy(schema, Node2)),
- ?match([], mnesia_test_lib:start_mnesia([Node1],[Tab2,Tab4])),
+ ?match([], mnesia_test_lib:start_mnesia([Node1],[Tab2,Tab4,Tab6])),
?match([], mnesia_test_lib:stop_mnesia([Node2])),
- ?match({atomic, ok},
- mnesia:del_table_copy(schema, Node2)),
+ ?match({atomic, ok}, mnesia:del_table_copy(schema, Node2)),
- %% Check
+ %% Check
RemNodes = AllNodes -- [Node2],
- ?match(RemNodes, mnesia:system_info(db_nodes)),
+ ?match(RemNodes, mnesia:system_info(db_nodes)),
?match([Node1], Who(Tab)),
?match(RemNodes, Who(Tab2)),
?match([Node3], Who(Tab3)),
?match([Node1], Who(Tab4)),
?match({'EXIT', {aborted, {no_exists, _, _}}}, Who(Tab5)),
+ ?match([Node1], Who(Tab6)),
- ?match({atomic, ok},
- mnesia:change_table_copy_type(Tab2, Node3, ram_copies)),
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab2, Node3, ram_copies)),
- ?match({atomic, ok},
- mnesia:change_table_copy_type(schema, Node3, ram_copies)),
+ ?match({atomic, ok}, mnesia:change_table_copy_type(schema, Node3, ram_copies)),
?match([], mnesia_test_lib:stop_mnesia([Node3])),
- ?match({atomic, ok},
- mnesia:del_table_copy(schema, Node3)),
- ?match([Node1], mnesia:system_info(db_nodes)),
+ ?match({atomic, ok}, mnesia:del_table_copy(schema, Node3)),
+ ?match([Node1], mnesia:system_info(db_nodes)),
?match([Node1], Who(Tab)),
?match([Node1], Who(Tab2)),
?match({'EXIT', {aborted, {no_exists, _, _}}}, Who(Tab3)),
@@ -363,7 +373,8 @@ start_and_stop(Config) when is_list(Config) ->
checkpoint(suite) -> [];
checkpoint(Config) when is_list(Config) ->
checkpoint(2, Config),
- checkpoint(3, Config).
+ checkpoint(3, Config),
+ ok.
checkpoint(NodeConfig, Config) ->
[Node1 | _] = TabNodes = ?acquire_nodes(NodeConfig, Config),
@@ -381,7 +392,7 @@ checkpoint(NodeConfig, Config) ->
CreateTab(Type, 3, [lists:last(TabNodes)])] ++
Acc
end,
- Types = [ram_copies, disc_copies, disc_only_copies],
+ Types = [ram_copies, disc_copies, disc_only_copies, ext_ets],
Tabs = lists:foldl(CreateTabs, [], Types),
Recs = ?sort([{T, N, N} || T <- Tabs, N <- lists:seq(1, 10)]),
lists:foreach(fun(R) -> ?match(ok, mnesia:dirty_write(R)) end, Recs),
@@ -430,25 +441,29 @@ checkpoint(NodeConfig, Config) ->
replica_location(suite) -> [];
replica_location(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
- Tab = replica_location,
%% Create three replicas
- Schema = [{name, Tab}, {disc_only_copies, [Node1]},
- {ram_copies, [Node2]}, {disc_copies, [Node3]}],
- ?match({atomic, ok}, mnesia:create_table(Schema)),
- ?match([], ?vrl(Tab, [Node1], [Node2], [Node3], Nodes)),
+ Check = fun(Tab, Schema) ->
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab}|Schema])),
+ ?match([], ?vrl(Tab, [Node1], [Node2], [Node3], Nodes)),
- %% Delete one replica
- ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node2)),
- ?match([], ?vrl(Tab, [Node1], [], [Node3], Nodes)),
+ %% Delete one replica
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node2)),
+ ?match([], ?vrl(Tab, [Node1], [], [Node3], Nodes)),
- %% Move one replica
- ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node1, Node2)),
- ?match([], ?vrl(Tab, [Node2], [], [Node3], Nodes)),
+ %% Move one replica
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node1, Node2)),
+ ?match([], ?vrl(Tab, [Node2], [], [Node3], Nodes)),
+
+ %% Change replica type
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node2, ram_copies)),
+ ?match([], ?vrl(Tab, [], [Node2], [Node3], Nodes))
+ end,
+ Check(replica_location, [{disc_only_copies, [Node1]},
+ {ram_copies, [Node2]}, {disc_copies, [Node3]}]),
- %% Change replica type
- ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node2, ram_copies)),
- ?match([], ?vrl(Tab, [], [Node2], [Node3], Nodes)),
+ Check(ext_location, [{disc_only_copies, [Node1]},
+ {ext_ets, [Node2]}, {disc_copies, [Node3]}]),
?verify_mnesia(Nodes, []).
@@ -720,7 +735,7 @@ replica_management(Config) when is_list(Config) ->
%%
?match({atomic, ok},
mnesia:create_table([{name, Tab}, {attributes, Attrs},
- {ram_copies, [Node1, Node3]}])),
+ {ram_copies, [Node1]}, {ext_ets, [Node3]}])),
[?match(ok, mnesia:dirty_write({Tab, K, K + 2})) || K <-lists:seq(1, 10)],
?match([], ?vrl(Tab, [], [Node1, Node3], [], Nodes)),
%% R - -
@@ -757,7 +772,7 @@ replica_management(Config) when is_list(Config) ->
?match([], ?vrl(Tab, [Node2], [], [Node1], Nodes)),
?match([0,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
%% D DO -
- ?match({atomic, ok}, mnesia:add_table_copy(Tab, Node3, ram_copies)),
+ ?match({atomic, ok}, mnesia:add_table_copy(Tab, Node3, ext_ets)),
?match([], ?vrl(Tab, [Node2], [Node3], [Node1], Nodes)),
?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
%% D DO R
@@ -784,7 +799,7 @@ replica_management(Config) when is_list(Config) ->
?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
%% D DO D0
- ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node3, ram_copies)),
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node3, ext_ets)),
?match([], ?vrl(Tab, [Node2], [Node3], [Node1], Nodes)),
?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
%% D DO R
@@ -841,18 +856,31 @@ replica_management(Config) when is_list(Config) ->
?match([], ?vrl(Tab, [Node3], [], [Node2], Nodes)),
?match([0,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
%% - D DO
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node3, ext_ets)),
+ ?match([], ?vrl(Tab, [], [Node3], [Node2], Nodes)),
+ ?match([0,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% - D ER
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node3, Node1)),
+ ?match([], ?vrl(Tab, [], [Node1], [Node2], Nodes)),
+ ?match([0,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% ER D -
+
?match({aborted, _}, mnesia:move_table_copy(Tab, Node1, Node2)),
+ ?match({aborted, _}, mnesia:move_table_copy(Tab, Node3, Node2)),
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node1, Node3)),
+ %% - D ER
?match([], mnesia_test_lib:stop_mnesia([Node3])),
?match({atomic,ok}, mnesia:transaction(fun() -> mnesia:write({Tab, 43, sync_me}) end)),
- ?match([], ?vrl(Tab, [Node3], [], [Node2],Nodes -- [Node3])),
- %% - D DO
+ ?match([], ?vrl(Tab, [], [Node3], [Node2],Nodes -- [Node3])),
+ %% - D ER
?match({aborted,Reason56} when element(1, Reason56) == not_active,
mnesia:move_table_copy(Tab, Node3, Node1)),
- ?match([], ?vrl(Tab, [Node3], [], [Node2],Nodes -- [Node3])),
- %% DO D -
+ ?match([], ?vrl(Tab, [], [Node3], [Node2],Nodes -- [Node3])),
+ %% - D ER
?match([], mnesia_test_lib:start_mnesia([Node3])),
- ?match([], ?vrl(Tab, [Node3], [], [Node2], Nodes)),
- %% DO D -
+ ?match([], ?vrl(Tab, [], [Node3], [Node2], Nodes)),
+ %% - D ER
+ ?match([{Tab,43,sync_me}], mnesia:dirty_read({Tab,43})),
%%
%% Transformer
@@ -990,7 +1018,7 @@ local_content(Config) when is_list(Config) ->
?match([], mnesia_test_lib:stop_mnesia([Node3])),
%% Added for OTP-44306
- ?match(ok, rpc:call(Node3, mnesia, start, [])),
+ ?match(ok, rpc:call(Node3, mnesia, start, [[{schema, ?BACKEND}]])),
?match({ok, _}, mnesia:change_config(extra_db_nodes, [Node3])),
mnesia_test_lib:sync_tables([Node3], [Tab1]),
@@ -1427,7 +1455,7 @@ unsupp_user_props(Config) when is_list(Config) ->
table_info, [silly1, user_properties])),
?match([{prop,propval2}], rpc:call(Node1, mnesia,
table_info, [silly2, user_properties])),
- ?match([{prop,propval3}], rpc:call(Node1, mnesia,
+ ?match([_,{prop,propval3}], rpc:call(Node1, mnesia,
table_info, [schema, user_properties])),
F2 = fun() ->
@@ -2262,6 +2290,10 @@ record_name_dirty_access_disc_only(suite) ->
record_name_dirty_access_disc_only(Config) when is_list(Config) ->
record_name_dirty_access(disc_only_copies, Config).
+record_name_dirty_access_xets(Config) when is_list(Config) ->
+ record_name_dirty_access(ext_ets, Config).
+
+
record_name_dirty_access(Storage, Config) ->
[Node1, _Node2] = Nodes = ?acquire_nodes(2, Config),
diff --git a/lib/mnesia/test/mnesia_recovery_test.erl b/lib/mnesia/test/mnesia_recovery_test.erl
index cd581787fb..2388b595d0 100644
--- a/lib/mnesia/test/mnesia_recovery_test.erl
+++ b/lib/mnesia/test/mnesia_recovery_test.erl
@@ -576,7 +576,7 @@ delete_during_start(Config) when is_list(Config) ->
mnesia_test_lib:kill_mnesia([N2,N3]),
%% timer:sleep(500),
?match({[ok,ok],[]}, rpc:multicall([N2,N3], mnesia,start,
- [[{extra_db_nodes,[N1]}]])),
+ [[{extra_db_nodes,[N1]}, {schema, ?BACKEND}]])),
[Tab1,Tab2,Tab3|_] = Tabs,
?match({atomic, ok}, mnesia:delete_table(Tab1)),
?match({atomic, ok}, mnesia:delete_table(Tab2)),
@@ -1542,7 +1542,7 @@ disc_less(Config) when is_list(Config) ->
?match(ok, rpc:call(Node2, mnesia, start, [])),
timer:sleep(500),
- ?match(ok, rpc:call(Node3, mnesia, start, [[{extra_db_nodes, [Node1, Node2]}]])),
+ ?match(ok, rpc:call(Node3, mnesia, start, [[{extra_db_nodes, [Node1, Node2]}, {schema, ?BACKEND}]])),
?match(ok, rpc:call(Node3, mnesia, wait_for_tables, [[Tab1, Tab2, Tab3], 20000])),
?match(ok, rpc:call(Node1, mnesia, wait_for_tables, [[Tab1, Tab2, Tab3], 20000])),
diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl
index 616207ed1a..6e84a27ec9 100644
--- a/lib/mnesia/test/mnesia_test_lib.erl
+++ b/lib/mnesia/test/mnesia_test_lib.erl
@@ -142,6 +142,9 @@
%% included for test server compatibility
%% assume that all test cases only takes Config as sole argument
init_per_testcase(_Func, Config) ->
+ Env = application:get_all_env(mnesia),
+ [application:unset_env(mnesia, Key, [{timeout, infinity}]) ||
+ {Key, _} <- Env, Key /= included_applications],
global:register_name(mnesia_global_logger, group_leader()),
Config.
@@ -668,11 +671,13 @@ do_prepare([delete_schema | Actions], Selected, All, Config, File, Line) ->
end,
do_prepare(Actions, Selected, All, Config, File, Line);
do_prepare([create_schema | Actions], Selected, All, Config, File, Line) ->
+ Ext = ?BACKEND,
case diskless(Config) of
true ->
+ rpc:multicall(Selected, application, set_env, [mnesia, schema, Ext]),
skip;
_Else ->
- case mnesia:create_schema(Selected) of
+ case mnesia:create_schema(Selected, Ext) of
ok ->
ignore;
BadNodes ->
@@ -975,7 +980,6 @@ reload_appls([Appl | Appls], Selected) ->
{Ok2temp, Empty} = rpc:multicall(Selected, application, unload, [Appl]),
Conv = fun({error,{not_loaded,mnesia}}) -> ok; (Else) -> Else end,
Ok2 = {lists:map(Conv, Ok2temp), Empty},
-
Ok3 = rpc:multicall(Selected, application, load, [Appl]),
if
Ok /= Ok2 ->
@@ -1040,7 +1044,8 @@ verify_replica_location(Tab, DiscOnly0, Ram0, Disc0, AliveNodes0) ->
S1 = ?match(AliveNodes, lists:sort(mnesia:system_info(running_db_nodes))),
S2 = ?match(DiscOnly, lists:sort(mnesia:table_info(Tab, disc_only_copies))),
- S3 = ?match(Ram, lists:sort(mnesia:table_info(Tab, ram_copies))),
+ S3 = ?match(Ram, lists:sort(mnesia:table_info(Tab, ram_copies) ++
+ mnesia:table_info(Tab, ext_ets))),
S4 = ?match(Disc, lists:sort(mnesia:table_info(Tab, disc_copies))),
S5 = ?match(Write, lists:sort(mnesia:table_info(Tab, where_to_write))),
S6 = case lists:member(This, Read) of
diff --git a/lib/mnesia/test/mnesia_test_lib.hrl b/lib/mnesia/test/mnesia_test_lib.hrl
index 714ec7a491..ba7eb10ea2 100644
--- a/lib/mnesia/test/mnesia_test_lib.hrl
+++ b/lib/mnesia/test/mnesia_test_lib.hrl
@@ -150,3 +150,5 @@
-define(verify_mnesia(Ups, Downs),
mnesia_test_lib:verify_mnesia(Ups, Downs, ?FILE, ?LINE)).
+
+-define(BACKEND, [{backend_types, [{ext_ets, ext_test},{ext_dets, ext_test}]}]).
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index 38fb8d6a77..aa50ee4cb1 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -931,20 +931,20 @@ index_update_bag(Config)when is_list(Config) ->
[IPos] = mnesia_lib:val({Tab,index}),
ITab = mnesia_lib:val({index_test,{index, IPos}}),
io:format("~n Index ~p @ ~p => ~p ~n~n",[IPos,ITab, ets:tab2list(ITab)]),
- ?match([{2,1},{2,2},{12,1}], lists:keysort(1,ets:tab2list(ITab))),
+ %?match([{2,1},{2,2},{12,1}], lists:keysort(1,ets:tab2list(ITab))),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
{atomic, R60} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1,Rec5,Rec2], lists:sort(R60)),
- ?match([{2,1},{2,2},{12,1}], lists:keysort(1,ets:tab2list(ITab))),
+ %?match([{2,1},{2,2},{12,1}], lists:keysort(1,ets:tab2list(ITab))),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec3) end)),
{atomic, R61} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1,Rec5,Rec2], lists:sort(R61)),
{atomic, R62} = mnesia:transaction(fun() -> mnesia:index_read(Tab,12, ValPos) end),
?match([], lists:sort(R62)),
- ?match([{2,1},{2,2}], lists:keysort(1,ets:tab2list(ITab))),
+ %% ?match([{2,1},{2,2}], lists:keysort(1,ets:tab2list(ITab))),
%% reset for rest of testcase
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
@@ -1142,8 +1142,9 @@ create_live_table_index(Config, Storage) ->
?match([{atomic,ok}|_], [Create(N) || N <- lists:seq(1,50)]),
?match([], mnesia_test_lib:stop_mnesia([N2,N3])),
- ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1]}]])),
- ?match(ok, rpc:call(N3, mnesia, start, [[{extra_db_nodes,[N1]}]])),
+ Ext = [{schema, ?BACKEND}],
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1]}|Ext]])),
+ ?match(ok, rpc:call(N3, mnesia, start, [[{extra_db_nodes,[N1]}|Ext]])),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index d37623d1cc..77609b11ce 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -53,25 +53,29 @@ start_link(Notebook, Parent) ->
init([Notebook, Parent]) ->
try
- Panel = wxPanel:new(Notebook),
+ TopP = wxPanel:new(Notebook),
Main = wxBoxSizer:new(?wxVERTICAL),
+ Panel = wxPanel:new(TopP),
+ GSzr = wxBoxSizer:new(?wxVERTICAL),
BorderFlags = ?wxLEFT bor ?wxRIGHT,
- Carrier = make_win(alloc, Panel, Main, BorderFlags bor ?wxTOP),
- Utilz = make_win(utilz, Panel, Main, BorderFlags),
-
- MemWin = {MemPanel,_} = create_mem_info(Panel),
- wxSizer:add(Main, MemPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM},
- {proportion, 1}, {border, 5}]),
- wxWindow:setSizer(Panel, Main),
+ Carrier = make_win(alloc, Panel, GSzr, BorderFlags bor ?wxTOP),
+ Utilz = make_win(utilz, Panel, GSzr, BorderFlags),
+ wxWindow:setSizer(Panel, GSzr),
+ wxSizer:add(Main, Panel, [{flag, ?wxEXPAND},{proportion,2}]),
+
+ MemWin = create_mem_info(TopP),
+ wxSizer:add(Main, MemWin, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM},
+ {proportion, 1}, {border, 5}]),
+ wxWindow:setSizer(TopP, Main),
Windows = [Carrier, Utilz],
PaintInfo = setup_graph_drawing(Windows),
- {Panel, #state{parent= Parent,
- panel = Panel,
- wins = Windows,
- mem = MemWin,
- paint = PaintInfo,
- time = setup_time()
- }
+ {TopP, #state{parent= Parent,
+ panel = Panel,
+ wins = Windows,
+ mem = MemWin,
+ paint = PaintInfo,
+ time = setup_time()
+ }
}
catch _:Err ->
io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
@@ -84,12 +88,14 @@ setup_time() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
handle_event(#wx{id=?ID_REFRESH_INTERVAL, event=#wxCommand{type=command_menu_selected}},
- #state{panel=Panel, appmon=Old, wins=Wins0, time=#ti{fetch=F0} = Ti0} = State) ->
+ #state{active=Active, panel=Panel, appmon=Old, wins=Wins0, time=#ti{fetch=F0} = Ti0} = State) ->
case interval_dialog(Panel, Ti0) of
Ti0 -> {noreply, State};
#ti{fetch=F0} = Ti -> %% Same fetch interval force refresh
Wins = [W#win{max=undefined} || W <- Wins0],
{noreply, precalc(State#state{time=Ti, wins=Wins})};
+ Ti when not Active ->
+ {noreply, State#state{time=Ti}};
Ti -> %% Changed fetch interval, drop all data
{noreply, restart_fetcher(Old, State#state{time=Ti})}
end;
@@ -120,7 +126,7 @@ handle_info({Key, {promise_reply, {badrpc, _}}}, #state{async=Key} = State) ->
{noreply, State#state{active=false, appmon=undefined}};
handle_info({Key, {promise_reply, SysInfo}},
- #state{async=Key, panel=Panel, samples=Data, active=Active, wins=Wins0,
+ #state{async=Key, panel=_Panel, samples=Data, active=Active, wins=Wins0,
time=#ti{tick=Tick, disp=Disp0}=Ti} = S0) ->
Disp = trunc(Disp0),
Next = max(Tick - Disp, 0),
@@ -131,7 +137,6 @@ handle_info({Key, {promise_reply, SysInfo}},
if Active ->
update_alloc(S0, Info),
State = precalc(S1),
- catch wxWindow:refresh(Panel),
{noreply, State};
true ->
{noreply, S1}
@@ -193,7 +198,8 @@ precalc(#state{samples=Data0, paint=Paint, time=Ti, wins=Wins0}=State) ->
State#state{wins=Wins}.
-update_alloc(#state{mem={_, Grid}}, Fields) ->
+update_alloc(#state{mem=Grid}, Fields) ->
+ wxWindow:freeze(Grid),
Max = wxListCtrl:getItemCount(Grid),
Update = fun({Name, BS, CS}, Row) ->
(Row >= Max) andalso wxListCtrl:insertItem(Grid, Row, ""),
@@ -203,6 +209,7 @@ update_alloc(#state{mem={_, Grid}}, Fields) ->
Row + 1
end,
wx:foldl(Update, 0, Fields),
+ wxWindow:thaw(Grid),
Fields.
alloc_info(SysInfo) ->
@@ -249,10 +256,9 @@ sum_alloc_one_instance([],BS,CS,TotalBS,TotalCS) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
create_mem_info(Parent) ->
- Panel = wxPanel:new(Parent),
- wxWindow:setBackgroundColour(Panel, {255,255,255}),
Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES,
- Grid = wxListCtrl:new(Panel, [{style, Style}]),
+ Grid = wxListCtrl:new(Parent, [{style, Style}]),
+
Li = wxListItem:new(),
AddListEntry = fun({Name, Align, DefSize}, Col) ->
wxListItem:setText(Li, Name),
@@ -267,12 +273,7 @@ create_mem_info(Parent) ->
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
- Sizer = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT},
- {border, 5}, {proportion, 1}]),
- wxWindow:setSizerAndFit(Panel, Sizer),
- {Panel, Grid}.
-
+ Grid.
create_menus(Parent, _) ->
View = {"View", [#create_menu{id = ?ID_REFRESH_INTERVAL, text = "Graph Settings"}]},
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index 0e01429aa7..cef83037d0 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -302,7 +302,9 @@ handle_info({delivery, _Pid, app, _Curr, {[], [], [], []}},
handle_info({delivery, Pid, app, Curr, AppData},
State = #state{panel=Panel, appmon=Pid, current=Curr, usegc=UseGC,
app_w=AppWin, paint=#paint{font=Font}}) ->
- GC = make_gc(AppWin, UseGC),
+ GC = if UseGC -> {?wxGC:create(AppWin), false};
+ true -> {false, wxWindowDC:new(AppWin)}
+ end,
setFont(GC, Font, {0,0,0}),
App = build_tree(AppData, GC),
destroy_gc(GC),
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index f0b8a004ec..c6a1c73c83 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -122,7 +122,7 @@ display_yes_no_dialog(Str) ->
%% display_info(Parent, [{Title, [{Label, Info}]}]) -> {Panel, Sizer, InfoFieldsToUpdate}
display_info(Frame, Info) ->
Panel = wxPanel:new(Frame),
- wxWindow:setBackgroundColour(Panel, {255,255,255}),
+ wxWindow:setBackgroundStyle(Panel, ?wxBG_STYLE_SYSTEM),
Sizer = wxBoxSizer:new(?wxVERTICAL),
wxSizer:addSpacer(Sizer, 5),
Add = fun(BoxInfo) ->
@@ -201,22 +201,21 @@ update_info2([Scroll = {_, _, _}|Fs], [{_, NewInfo}|Rest]) ->
update_scroll_boxes(Scroll, NewInfo),
update_info2(Fs, Rest);
update_info2([Field|Fs], [{_Str, {click, Value}}|Rest]) ->
- wxTextCtrl:setValue(Field, to_str(Value)),
+ wxStaticText:setLabel(Field, to_str(Value)),
update_info2(Fs, Rest);
update_info2([Field|Fs], [{_Str, Value}|Rest]) ->
- wxTextCtrl:setValue(Field, to_str(Value)),
+ wxStaticText:setLabel(Field, to_str(Value)),
update_info2(Fs, Rest);
update_info2([Field|Fs], [undefined|Rest]) ->
- wxTextCtrl:setValue(Field, ""),
+ wxStaticText:setLabel(Field, ""),
update_info2(Fs, Rest);
update_info2([], []) -> ok.
update_scroll_boxes({_, _, 0}, {_, []}) -> ok;
update_scroll_boxes({Win, Sizer, _}, {Type, List}) ->
[wxSizerItem:deleteWindows(Child) || Child <- wxSizer:getChildren(Sizer)],
- BC = wxWindow:getBackgroundColour(Win),
Cursor = wxCursor:new(?wxCURSOR_HAND),
- add_entries(Type, List, Win, Sizer, BC, Cursor),
+ add_entries(Type, List, Win, Sizer, Cursor),
wxCursor:destroy(Cursor),
wxSizer:recalcSizes(Sizer),
wxWindow:refresh(Win),
@@ -379,25 +378,22 @@ add_box(Panel, OuterBox, Cursor, Title, Proportion, {Format, List}) ->
wxScrolledWindow:setScrollbars(Scroll,1,1,0,0),
ScrollSizer = wxBoxSizer:new(?wxVERTICAL),
wxScrolledWindow:setSizer(Scroll, ScrollSizer),
- BC = wxWindow:getBackgroundColour(Panel),
- wxWindow:setBackgroundColour(Scroll,BC),
- add_entries(Format, List, Scroll, ScrollSizer, BC, Cursor),
+ wxWindow:setBackgroundStyle(Scroll, ?wxBG_STYLE_SYSTEM),
+ add_entries(Format, List, Scroll, ScrollSizer, Cursor),
wxSizer:add(Box,Scroll,[{proportion,1},{flag,?wxEXPAND}]),
wxSizer:add(OuterBox,Box,[{proportion,Proportion},{flag,?wxEXPAND}]),
{Scroll,ScrollSizer,length(List)}.
-add_entries(click, List, Scroll, ScrollSizer, BC, Cursor) ->
+add_entries(click, List, Scroll, ScrollSizer, Cursor) ->
Add = fun(Link) ->
TC = link_entry(Scroll, Link, Cursor),
- wxWindow:setBackgroundColour(TC,BC),
- wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}])
+ wxWindow:setBackgroundStyle(TC, ?wxBG_STYLE_SYSTEM),
+ wxSizer:add(ScrollSizer,TC, [{flag,?wxEXPAND}])
end,
[Add(Link) || Link <- List];
-add_entries(plain, List, Scroll, ScrollSizer, _, _) ->
+add_entries(plain, List, Scroll, ScrollSizer, _) ->
Add = fun(String) ->
- TC = wxTextCtrl:new(Scroll, ?wxID_ANY,
- [{style,?SINGLE_LINE_STYLE},
- {value,String}]),
+ TC = wxStaticText:new(Scroll, ?wxID_ANY, String),
wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}])
end,
[Add(String) || String <- List].
@@ -435,51 +431,44 @@ create_box(Panel, {scroll_boxes,Data}) ->
wxSizer:layout(OuterBox),
{OuterBox, Boxes};
-create_box(Panel, Data) ->
- {Title, Align, Info} = get_box_info(Data),
- Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, Title}]),
- LeftSize = get_max_size(Panel,Info),
- LeftProportion = [{proportion,0}],
- RightProportion = [{proportion,1}, {flag, Align bor ?wxEXPAND}],
+create_box(Parent, Data) ->
+ {Title, _Align, Info} = get_box_info(Data),
+ Top = wxStaticBoxSizer:new(?wxVERTICAL, Parent, [{label, Title}]),
+ Panel = wxPanel:new(Parent),
+ Box = wxBoxSizer:new(?wxVERTICAL),
+ LeftSize = 30 + get_max_width(Panel,Info),
+ RightProportion = [{flag, ?wxEXPAND}],
AddRow = fun({Desc0, Value0}) ->
Desc = Desc0++":",
Line = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Line,
- wxTextCtrl:new(Panel, ?wxID_ANY,
- [{style,?SINGLE_LINE_STYLE},
- {size,LeftSize},
- {value,Desc}]),
- LeftProportion),
+ Label = wxStaticText:new(Panel, ?wxID_ANY, Desc),
+ wxSizer:add(Line, 5, 0),
+ wxSizer:add(Line, Label),
+ wxSizer:setItemMinSize(Line, Label, LeftSize, -1),
Field =
case Value0 of
{click,"unknown"} ->
- wxTextCtrl:new(Panel, ?wxID_ANY,
- [{style,?SINGLE_LINE_STYLE},
- {value,"unknown"}]);
+ wxStaticText:new(Panel, ?wxID_ANY,"unknown");
{click,Value} ->
link_entry(Panel,Value);
_ ->
Value = to_str(Value0),
- TCtrl = wxTextCtrl:new(Panel, ?wxID_ANY,
- [{style,?SINGLE_LINE_STYLE},
- {value,Value}]),
+ TCtrl = wxStaticText:new(Panel, ?wxID_ANY,Value),
length(Value) > 50 andalso
wxWindow:setToolTip(TCtrl,wxToolTip:new(Value)),
TCtrl
end,
wxSizer:add(Line, 10, 0), % space of size 10 horisontally
wxSizer:add(Line, Field, RightProportion),
-
- {_,H,_,_} = wxTextCtrl:getTextExtent(Field,"Wj"),
- wxTextCtrl:setMinSize(Field,{0,H}),
-
- wxSizer:add(Box, Line, [{proportion,0},{flag,?wxEXPAND}]),
+ wxSizer:add(Box, Line, [{proportion,1},{flag,?wxEXPAND}]),
Field;
(undefined) ->
undefined
end,
InfoFields = [AddRow(Entry) || Entry <- Info],
- {Box, InfoFields}.
+ wxWindow:setSizer(Panel, Box),
+ wxSizer:add(Top, Panel, [{proportion,1},{flag,?wxEXPAND}]),
+ {Top, InfoFields}.
link_entry(Panel, Link) ->
Cursor = wxCursor:new(?wxCURSOR_HAND),
@@ -490,13 +479,12 @@ link_entry(Panel, Link, Cursor) ->
link_entry2(Panel, to_link(Link), Cursor).
link_entry2(Panel,{Target,Str},Cursor) ->
- TC = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, ?SINGLE_LINE_STYLE}]),
- wxTextCtrl:setForegroundColour(TC,?wxBLUE),
- wxTextCtrl:appendText(TC, Str),
+ TC = wxStaticText:new(Panel, ?wxID_ANY, Str),
+ wxWindow:setForegroundColour(TC,?wxBLUE),
wxWindow:setCursor(TC, Cursor),
- wxTextCtrl:connect(TC, left_down, [{userData,Target}]),
- wxTextCtrl:connect(TC, enter_window),
- wxTextCtrl:connect(TC, leave_window),
+ wxWindow:connect(TC, left_down, [{userData,Target}]),
+ wxWindow:connect(TC, enter_window),
+ wxWindow:connect(TC, leave_window),
ToolTip = wxToolTip:new("Click to see properties for " ++ Str),
wxWindow:setToolTip(TC, ToolTip),
TC.
@@ -521,23 +509,12 @@ html_window(Panel, Html) ->
wxHtmlWindow:setPage(Win, Html),
Win.
-get_max_size(Panel,Info) ->
- Txt = wxTextCtrl:new(Panel, ?wxID_ANY, []),
- Size = get_max_size(Txt,Info,0,0),
- wxTextCtrl:destroy(Txt),
- Size.
-
-get_max_size(Txt,[{Desc,_}|Info],MaxX,MaxY) ->
- {X,Y,_,_} = wxTextCtrl:getTextExtent(Txt,Desc++":"),
- if X>MaxX ->
- get_max_size(Txt,Info,X,Y);
- true ->
- get_max_size(Txt,Info,MaxX,MaxY)
- end;
-get_max_size(Txt,[undefined|Info],MaxX,MaxY) ->
- get_max_size(Txt,Info,MaxX,MaxY);
-get_max_size(_,[],X,_Y) ->
- {X+2,-1}.
+get_max_width(Parent,Info) ->
+ lists:foldl(fun({Desc,_}, Max) ->
+ {W, _, _, _} = wxWindow:getTextExtent(Parent, Desc),
+ max(W,Max);
+ (_, Max) -> Max
+ end, 0, Info).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set_listctrl_col_size(LCtrl, Total) ->
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 017f07d2c4..1010a6af4c 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -100,7 +100,7 @@ setup_graph_drawing(Panels) ->
IsWindows = element(1, os:type()) =:= win32,
IgnoreCB = {callback, fun(_,_) -> ok end},
Do = fun(#win{panel=Panel}) ->
- wxWindow:setBackgroundColour(Panel, ?wxWHITE),
+ wxWindow:setBackgroundStyle(Panel, ?wxBG_STYLE_SYSTEM),
wxPanel:connect(Panel, paint, [callback]),
IsWindows andalso
wxPanel:connect(Panel, erase_background, [IgnoreCB])
@@ -142,6 +142,8 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL, event=#wxCommand{type=command_menu_sel
#ti{fetch=F0} = Ti -> %% Same fetch interval force refresh
Wins = [W#win{max=undefined} || W <- Wins0],
{noreply, precalc(State#state{time=Ti, wins=Wins})};
+ Ti when Old =:= undefined ->
+ {noreply, State#state{time=Ti}};
Ti -> %% Changed fetch interval, drop all data
{noreply, restart_fetcher(node(Old), State#state{time=Ti})}
end;
@@ -762,7 +764,10 @@ make_gc(Panel,UseGC) ->
destroy_gc({GC, DC}) ->
(GC =/= false) andalso ?wxGC:destroy(GC),
- wxPaintDC:destroy(DC).
+ case DC =/= false andalso wx:getObjectType(DC) of
+ false -> ok;
+ Type -> Type:destroy(DC)
+ end.
haveGC() ->
try
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 9d6aa84384..cff5fbb474 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -125,11 +125,11 @@ handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) ->
{noreply, State};
handle_event(#wx{obj=Obj, event=#wxMouse{type=enter_window}}, State) ->
- wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}),
+ wxStaticText:setForegroundColour(Obj,{0,0,100,255}),
{noreply, State};
handle_event(#wx{obj=Obj, event=#wxMouse{type=leave_window}}, State) ->
- wxTextCtrl:setForegroundColour(Obj,?wxBLUE),
+ wxStaticText:setForegroundColour(Obj,?wxBLUE),
{noreply, State};
handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}},
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index a795c4fe25..73ed890802 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -101,7 +101,7 @@ end_per_group(_GroupName, Config) ->
init_per_suite(Config) when is_list(Config) ->
delete_saved(Config),
DataDir = ?config(data_dir,Config),
- Rels = [R || R <- [r16b,'17'], ?t:is_release_available(R)] ++ [current],
+ Rels = [R || R <- ['17','18'], ?t:is_release_available(R)] ++ [current],
io:format("Creating crash dumps for the following releases: ~p", [Rels]),
AllDumps = create_dumps(DataDir,Rels),
[{dumps,AllDumps}|Config].
@@ -606,21 +606,21 @@ dos_dump(DataDir,Rel,Dump) ->
rel_opt(Rel) ->
case Rel of
- r16b -> [{erl,[{release,"r16b_latest"}]}];
'17' -> [{erl,[{release,"17_latest"}]}];
+ '18' -> [{erl,[{release,"18_latest"}]}];
current -> []
end.
dump_prefix(Rel) ->
case Rel of
- r16b -> "r16b_dump.";
'17' -> "r17_dump.";
- current -> "r18_dump."
+ '18' -> "r18_dump.";
+ current -> "r19_dump."
end.
compat_rel(Rel) ->
case Rel of
- r16b -> "+R16 ";
'17' -> "+R17 ";
+ '18' -> "+R18 ";
current -> ""
end.
diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl
index 499cdb3fc8..c06ec21f36 100644
--- a/lib/observer/test/ttb_SUITE.erl
+++ b/lib/observer/test/ttb_SUITE.erl
@@ -819,6 +819,7 @@ myhandler(_Fd,Trace,_,Relay) ->
simple_call_handler() ->
{fun(A, {trace_ts, _, call, _, _} ,_,_) -> io:format(A, "ok.~n", []);
+ (A, {drop, N}, _, _) -> io:format(A, "{drop, ~p}.", [N]);
(_, end_of_trace, _, _) -> ok end, []}.
marking_call_handler() ->
@@ -954,17 +955,24 @@ begin_trace_local(ServerNode, ClientNode, Dest) ->
?line ttb:tpl(client, get, []).
check_size(N, Dest, Output, ServerNode, ClientNode) ->
- ?line begin_trace(ServerNode, ClientNode, Dest),
- ?line case Dest of
+ begin_trace(ServerNode, ClientNode, Dest),
+ case Dest of
{local, _} ->
- ?line ttb_helper:msgs_ip(N);
+ ttb_helper:msgs_ip(N);
_ ->
- ?line ttb_helper:msgs(N)
+ ttb_helper:msgs(N)
end,
- ?line {_, D} = ttb:stop([fetch, return_fetch_dir]),
- ?line ttb:format(D, [{out, Output}, {handler, simple_call_handler()}]),
- ?line {ok, Ret} = file:consult(Output),
- ?line true = (N + 1 == length(Ret)).
+ {_, D} = ttb:stop([fetch, return_fetch_dir]),
+ ttb:format(D, [{out, Output}, {handler, simple_call_handler()}]),
+ {ok, Ret} = file:consult(Output),
+ check_output(N+1, Ret).
+
+check_output(Expected, Ret)
+ when length(Ret) =:= Expected -> ok;
+check_output(Expected, Ret) ->
+ io:format("~p~n",[Ret]),
+ io:format("Expected ~p got ~p ~n",[Expected, length(Ret)]),
+ Expected = length(Ret).
fetch_when_no_option_given(suite) ->
[];
@@ -1166,8 +1174,8 @@ changing_cwd_on_control_node(Config) when is_list(Config) ->
?line {_, D} = ttb:stop([fetch, return_fetch_dir]),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
- ?line true = (2*(NumMsgs + 1) == length(Ret)),
- ?line ok = file:set_cwd(OldDir).
+ check_output(2*(NumMsgs + 1),Ret),
+ ok = file:set_cwd(OldDir).
changing_cwd_on_control_node(cleanup,_Config) ->
?line stop_client_and_server().
@@ -1176,18 +1184,19 @@ changing_cwd_on_control_node_with_local_trace(suite) ->
changing_cwd_on_control_node_with_local_trace(doc) ->
["Changing cwd on control node during local tracing is safe"];
changing_cwd_on_control_node_with_local_trace(Config) when is_list(Config) ->
- ?line {ok, OldDir} = file:get_cwd(),
- ?line {ServerNode, ClientNode} = start_client_and_server(),
- ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}),
- ?line NumMsgs = 3,
- ?line ttb_helper:msgs_ip(NumMsgs),
- ?line ok = file:set_cwd(".."),
- ?line ttb_helper:msgs_ip(NumMsgs),
- ?line {_, D} = ttb:stop([fetch, return_fetch_dir]),
- ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
- ?line {ok, Ret} = file:consult(?OUTPUT),
- ?line true = (2*(NumMsgs + 1) == length(Ret)),
- ?line ok = file:set_cwd(OldDir).
+ {ok, OldDir} = file:get_cwd(),
+ {ServerNode, ClientNode} = start_client_and_server(),
+ begin_trace(ServerNode, ClientNode, {local, ?FNAME}),
+ NumMsgs = 3,
+ ttb_helper:msgs_ip(NumMsgs),
+ ok = file:set_cwd(".."),
+ ttb_helper:msgs_ip(NumMsgs),
+ {_, D} = ttb:stop([fetch, return_fetch_dir]),
+ ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
+ {ok, Ret} = file:consult(?OUTPUT),
+ Expected = 2*(NumMsgs + 1),
+ check_output(Expected, Ret),
+ ok = file:set_cwd(OldDir).
changing_cwd_on_control_node_with_local_trace(cleanup,_Config) ->
?line stop_client_and_server().
@@ -1205,7 +1214,7 @@ changing_cwd_on_remote_node(Config) when is_list(Config) ->
?line {_, D} = ttb:stop([fetch, return_fetch_dir]),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
- ?line true = (2*(NumMsgs + 1) == length(Ret)).
+ check_output(2*(NumMsgs + 1),Ret).
changing_cwd_on_remote_node(cleanup,_Config) ->
?line stop_client_and_server().
@@ -1497,7 +1506,7 @@ logic(N, M, TracingType) ->
ct:log("formatted ~p",[{D,?OUTPUT}]),
?line {ok, Ret} = file:consult(?OUTPUT),
ct:log("consulted: ~p",[Ret]),
- ?line M = length(Ret).
+ check_output(M,Ret).
begin_trace_with_resume(ServerNode, ClientNode, Dest) ->
?line {ok, _} = ttb:tracer([ServerNode,ClientNode], [{file, Dest}, resume]),
diff --git a/lib/os_mon/c_src/ferrule.c b/lib/os_mon/c_src/ferrule.c
index 8c44e52aba..47f8a21270 100644
--- a/lib/os_mon/c_src/ferrule.c
+++ b/lib/os_mon/c_src/ferrule.c
@@ -18,6 +18,7 @@
* %CopyrightEnd%
*/
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <stropts.h>
#include <poll.h>
diff --git a/lib/os_mon/c_src/mod_syslog.c b/lib/os_mon/c_src/mod_syslog.c
index e287ac9090..720b52916e 100644
--- a/lib/os_mon/c_src/mod_syslog.c
+++ b/lib/os_mon/c_src/mod_syslog.c
@@ -18,6 +18,7 @@
* %CopyrightEnd%
*/
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl
index 0045b2c09f..5664615230 100644
--- a/lib/os_mon/src/cpu_sup.erl
+++ b/lib/os_mon/src/cpu_sup.erl
@@ -68,7 +68,7 @@
-type util_cpus() :: 'all' | integer() | [integer()].
-type util_state() :: 'user' | 'nice_user' | 'kernel' | 'wait' | 'idle'.
--type util_value() :: {util_state(), float()} | float().
+-type util_value() :: [{util_state(), float()}] | float().
-type util_desc() :: {util_cpus(), util_value(), util_value(), []}.
%%----------------------------------------------------------------------
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 6923066da7..04daee460f 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -118,7 +118,7 @@
<p><c> not_encrypted | cipher_info()}</c></p></item>
<tag><c>cipher_info() = </c></tag>
- <item><p><c>{"RC2-CBC" | "DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)</c></p>
+ <item><p><c>{"RC2-CBC" | "DES-CBC" | "DES-EDE3-CBC", crypto:strong_rand_bytes(8)</c></p>
<p><c>| {#'PBEParameter{}, digest_type()} | #'PBES2-params'{}}</c></p>
</item>
diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml
index 86808adbea..e3a1eed4be 100644
--- a/lib/public_key/doc/src/using_public_key.xml
+++ b/lib/public_key/doc/src/using_public_key.xml
@@ -124,7 +124,7 @@
...&gt;&gt;,
not_encrypted},
{'Certificate',&lt;&lt;48,130,3,200,48,130,3,49,160,3,2,1,2,2,1,
- 1,48,13,6,9,42,134,72,134,247,...&gt;&gt;>,
+ 1,48,13,6,9,42,134,72,134,247,...&gt;&gt;,
not_encrypted}]</code>
<p>Certificates can be decoded as usual:</p>
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index be1a4472e9..51050c4480 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -199,7 +199,7 @@ encrypted_pem(Config) when is_list(Config) ->
RSAKey = public_key:der_decode('RSAPrivateKey', DerRSAKey),
- Salt0 = crypto:rand_bytes(8),
+ Salt0 = crypto:strong_rand_bytes(8),
Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey,
{{"DES-EDE3-CBC", Salt0}, "1234abcd"}),
RSAKey = public_key:pem_entry_decode(Entry0,"1234abcd"),
@@ -208,7 +208,7 @@ encrypted_pem(Config) when is_list(Config) ->
[{'RSAPrivateKey', _, {"DES-EDE3-CBC", Salt0}}] =
erl_make_certs:pem_to_der(Des3KeyFile),
- Salt1 = crypto:rand_bytes(8),
+ Salt1 = crypto:strong_rand_bytes(8),
Entry1 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey,
{{"DES-CBC", Salt1}, "4567efgh"}),
DesKeyFile = filename:join(Datadir, "des_client_key.pem"),
diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in
index 70b48daf97..4530a83aee 100644
--- a/lib/runtime_tools/c_src/Makefile.in
+++ b/lib/runtime_tools/c_src/Makefile.in
@@ -91,7 +91,7 @@ $(OBJDIR):
$(LIBDIR):
-@mkdir -p $(LIBDIR)
-$(OBJDIR)/%$(TYPEMARKER).o: %.c
+$(OBJDIR)/%$(TYPEMARKER).o: %.c dyntrace_lttng.h
$(V_CC) -c -o $@ $(ALL_CFLAGS) $<
$(LIBDIR)/%$(TYPEMARKER).@DED_EXT@: $(OBJDIR)/%$(TYPEMARKER).o
diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c
index 0ef8eaf4d3..0178d95efb 100644
--- a/lib/runtime_tools/c_src/dyntrace.c
+++ b/lib/runtime_tools/c_src/dyntrace.c
@@ -29,7 +29,13 @@
#include "sys.h"
#include "dtrace-wrapper.h"
#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP))
-#define HAVE_USE_DTRACE 1
+# define HAVE_USE_DTRACE 1
+#endif
+#if defined(USE_LTTNG)
+# define HAVE_USE_LTTNG 1
+# define TRACEPOINT_DEFINE
+# define TRACEPOINT_CREATE_PROBES
+# include "dyntrace_lttng.h"
#endif
void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf);
@@ -60,11 +66,56 @@ static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+#ifdef HAVE_USE_LTTNG
+static ERL_NIF_TERM trace_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace_running_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace_running_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace_receive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace_garbage_collection(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM enabled_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled_running_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled_running_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled_receive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM enabled_garbage_collection(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+#endif
+
+
static ErlNifFunc nif_funcs[] = {
{"available", 0, available},
{"user_trace_s1", 1, user_trace_s1},
{"user_trace_i4s4", 9, user_trace_i4s4},
- {"user_trace_n", 10, user_trace_n}
+ {"user_trace_n", 10, user_trace_n},
+#ifdef HAVE_USE_LTTNG
+ {"trace_procs", 6, trace_procs},
+ {"trace_ports", 6, trace_ports},
+ {"trace_running_procs", 6, trace_running_procs},
+ {"trace_running_ports", 6, trace_running_ports},
+ {"trace_call", 6, trace_call},
+ {"trace_send", 6, trace_send},
+ {"trace_receive", 6, trace_receive},
+ {"trace_garbage_collection", 6, trace_garbage_collection},
+ {"enabled_procs", 3, enabled_procs},
+ {"enabled_ports", 3, enabled_ports},
+ {"enabled_running_procs", 3, enabled_running_procs},
+ {"enabled_running_ports", 3, enabled_running_ports},
+ {"enabled_call", 3, enabled_call},
+ {"enabled_send", 3, enabled_send},
+ {"enabled_receive", 3, enabled_receive},
+ {"enabled_garbage_collection", 3, enabled_garbage_collection},
+#endif
+ {"enabled", 3, enabled},
+ {"trace", 5, trace},
+ {"trace", 6, trace}
};
ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL)
@@ -76,6 +127,61 @@ static ERL_NIF_TERM atom_not_available;
static ERL_NIF_TERM atom_badarg;
static ERL_NIF_TERM atom_ok;
+static ERL_NIF_TERM atom_trace;
+static ERL_NIF_TERM atom_seq_trace;
+static ERL_NIF_TERM atom_remove;
+static ERL_NIF_TERM atom_discard;
+
+#ifdef HAVE_USE_LTTNG
+
+/* gc atoms */
+
+static ERL_NIF_TERM atom_gc_minor_start;
+static ERL_NIF_TERM atom_gc_minor_end;
+static ERL_NIF_TERM atom_gc_major_start;
+static ERL_NIF_TERM atom_gc_major_end;
+
+static ERL_NIF_TERM atom_old_heap_block_size; /* for debug */
+static ERL_NIF_TERM atom_heap_block_size; /* for debug */
+
+/* process 'procs' */
+
+static ERL_NIF_TERM atom_spawn;
+static ERL_NIF_TERM atom_exit;
+static ERL_NIF_TERM atom_register;
+static ERL_NIF_TERM atom_unregister;
+static ERL_NIF_TERM atom_link;
+static ERL_NIF_TERM atom_unlink;
+static ERL_NIF_TERM atom_getting_linked;
+static ERL_NIF_TERM atom_getting_unlinked;
+
+/* process 'running' and 'exiting' */
+
+static ERL_NIF_TERM atom_in;
+static ERL_NIF_TERM atom_out;
+static ERL_NIF_TERM atom_in_exiting;
+static ERL_NIF_TERM atom_out_exiting;
+static ERL_NIF_TERM atom_out_exited;
+
+/* process messages 'send' and 'receive' */
+
+static ERL_NIF_TERM atom_send;
+static ERL_NIF_TERM atom_receive;
+static ERL_NIF_TERM atom_send_to_non_existing_process;
+
+/* ports 'ports' */
+
+static ERL_NIF_TERM atom_open;
+static ERL_NIF_TERM atom_closed;
+
+/* 'call' */
+
+static ERL_NIF_TERM atom_call;
+static ERL_NIF_TERM atom_return_from;
+static ERL_NIF_TERM atom_return_to;
+static ERL_NIF_TERM atom_exception_from;
+#endif
+
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
atom_true = enif_make_atom(env,"true");
@@ -85,6 +191,61 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_badarg = enif_make_atom(env,"badarg");
atom_ok = enif_make_atom(env,"ok");
+ atom_trace = enif_make_atom(env,"trace");
+ atom_seq_trace = enif_make_atom(env,"seq_trace");
+ atom_remove = enif_make_atom(env,"remove");
+ atom_discard = enif_make_atom(env,"discard");
+
+#ifdef HAVE_USE_LTTNG
+
+ /* gc */
+
+ atom_gc_minor_start = enif_make_atom(env,"gc_minor_start");
+ atom_gc_minor_end = enif_make_atom(env,"gc_minor_end");
+ atom_gc_major_start = enif_make_atom(env,"gc_major_start");
+ atom_gc_major_end = enif_make_atom(env,"gc_major_end");
+
+ atom_old_heap_block_size = enif_make_atom(env,"old_heap_block_size");
+ atom_heap_block_size = enif_make_atom(env,"heap_block_size");
+
+ /* process 'proc' */
+
+ atom_spawn = enif_make_atom(env,"spawn");
+ atom_exit = enif_make_atom(env,"exit");
+ atom_register = enif_make_atom(env,"register");
+ atom_unregister = enif_make_atom(env,"unregister");
+ atom_link = enif_make_atom(env,"link");
+ atom_unlink = enif_make_atom(env,"unlink");
+ atom_getting_unlinked = enif_make_atom(env,"getting_unlinked");
+ atom_getting_linked = enif_make_atom(env,"getting_linked");
+
+ /* process 'running' and 'exiting' */
+
+ atom_in = enif_make_atom(env,"in");
+ atom_out = enif_make_atom(env,"out");
+ atom_in_exiting = enif_make_atom(env,"in_exiting");
+ atom_out_exiting = enif_make_atom(env,"out_exiting");
+ atom_out_exited = enif_make_atom(env,"out_exited");
+
+ /* process messages 'send' and 'receive' */
+
+ atom_send = enif_make_atom(env,"send");
+ atom_receive = enif_make_atom(env,"receive");
+ atom_send_to_non_existing_process = enif_make_atom(env,"send_to_non_existing_process");
+
+ /* ports 'ports' */
+
+ atom_open = enif_make_atom(env,"open");
+ atom_closed = enif_make_atom(env,"closed");
+
+ /* 'call' */
+
+ atom_call = enif_make_atom(env,"call");
+ atom_return_from = enif_make_atom(env,"return_from");
+ atom_return_to = enif_make_atom(env,"return_to");
+ atom_exception_from = enif_make_atom(env,"exception_from");
+#endif
+
return 0;
}
@@ -123,3 +284,442 @@ static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
return atom_error;
#endif
}
+
+static ERL_NIF_TERM enabled(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+#ifdef HAVE_USE_LTTNG
+ ASSERT(argc == 3);
+ return atom_trace;
+#endif
+ return atom_remove;
+}
+
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return atom_ok;
+}
+
+#ifdef HAVE_USE_LTTNG
+static ERL_NIF_TERM enabled_garbage_collection(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ ASSERT(argc == 3);
+
+ if (argv[0] == atom_gc_minor_start && LTTNG_ENABLED(gc_minor_start)) {
+ return atom_trace;
+ } else if (argv[0] == atom_gc_minor_end && LTTNG_ENABLED(gc_minor_end)) {
+ return atom_trace;
+ } else if (argv[0] == atom_gc_major_start && LTTNG_ENABLED(gc_major_start)) {
+ return atom_trace;
+ } else if (argv[0] == atom_gc_major_end && LTTNG_ENABLED(gc_major_end)) {
+ return atom_trace;
+ }
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_garbage_collection(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ lttng_decl_procbuf(pid);
+ ERL_NIF_TERM gci, tup;
+ const ERL_NIF_TERM *vals;
+ int arity;
+ unsigned long ohbsz, nhbsz, size;
+
+ ASSERT(argc == 6);
+
+ /* Assume gc info order does not change */
+ gci = argv[3];
+
+ /* get reclaimed or need */
+ enif_get_list_cell(env, gci, &tup, &gci);
+ enif_get_tuple(env, tup, &arity, &vals);
+ ASSERT(arity == 2);
+ enif_get_ulong(env, vals[1], &size);
+
+ /* get old heap block size */
+ enif_get_list_cell(env, gci, &tup, &gci);
+ enif_get_tuple(env, tup, &arity, &vals);
+ ASSERT(arity == 2);
+ ASSERT(vals[0] == atom_old_heap_block_size);
+ enif_get_ulong(env, vals[1], &ohbsz);
+
+ /* get new heap block size */
+ enif_get_list_cell(env, gci, &tup, &gci);
+ enif_get_tuple(env, tup, &arity, &vals);
+ ASSERT(arity == 2);
+ ASSERT(vals[0] == atom_heap_block_size);
+ enif_get_ulong(env, vals[1], &nhbsz);
+
+ lttng_pid_to_str(argv[2], pid);
+
+ if (argv[0] == atom_gc_minor_start) {
+ LTTNG4(gc_minor_start, pid, size, nhbsz, ohbsz);
+ } else if (argv[0] == atom_gc_minor_end) {
+ LTTNG4(gc_minor_end, pid, size, nhbsz, ohbsz);
+ } else if (argv[0] == atom_gc_major_start) {
+ LTTNG4(gc_major_start, pid, size, nhbsz, ohbsz);
+ } else if (argv[0] == atom_gc_major_end) {
+ LTTNG4(gc_major_end, pid, size, nhbsz, ohbsz);
+ }
+ return atom_ok;
+}
+
+static ERL_NIF_TERM enabled_call(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ ASSERT(argc == 3);
+
+ if (argv[0] == atom_call && LTTNG_ENABLED(function_call))
+ return atom_trace;
+ else if (argv[0] == atom_return_from && LTTNG_ENABLED(function_return))
+ return atom_trace;
+ else if (argv[0] == atom_exception_from && LTTNG_ENABLED(function_exception))
+ return atom_trace;
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ lttng_decl_procbuf(pid);
+ unsigned int len;
+ char undef[] = "undefined";
+
+ lttng_pid_to_str(argv[2], pid);
+
+ if (argv[0] == atom_call) {
+ const ERL_NIF_TERM* tuple;
+ int arity;
+ lttng_decl_mfabuf(mfa);
+
+ if (enif_get_tuple(env, argv[3], &arity, &tuple)) {
+ if (enif_is_list(env, tuple[2])) {
+ enif_get_list_length(env, tuple[2], &len);
+ } else {
+ enif_get_uint(env, tuple[2], &len);
+ }
+ lttng_mfa_to_str(tuple[0], tuple[1], len, mfa);
+ LTTNG3(function_call, pid, mfa, 0);
+ } else {
+ LTTNG3(function_call, pid, undef, 0);
+ }
+ } else if (argv[0] == atom_return_from) {
+ const ERL_NIF_TERM* tuple;
+ int arity;
+ lttng_decl_mfabuf(mfa);
+
+ if (enif_get_tuple(env, argv[3], &arity, &tuple)) {
+ enif_get_uint(env, tuple[2], &len);
+ lttng_mfa_to_str(tuple[0], tuple[1], len, mfa);
+ LTTNG3(function_return, pid, mfa, 0);
+ } else {
+ LTTNG3(function_return, pid, undef, 0);
+ }
+ } else if (argv[0] == atom_return_to) {
+ const ERL_NIF_TERM* tuple;
+ int arity;
+ lttng_decl_mfabuf(mfa);
+
+ if (enif_get_tuple(env, argv[3], &arity, &tuple)) {
+ enif_get_uint(env, tuple[2], &len);
+ lttng_mfa_to_str(tuple[0], tuple[1], len, mfa);
+ LTTNG3(function_return, pid, mfa, 0);
+ } else {
+ LTTNG3(function_return, pid, undef, 0);
+ }
+ } else if (argv[0] == atom_exception_from) {
+ const ERL_NIF_TERM* tuple;
+ int arity;
+ lttng_decl_mfabuf(mfa);
+ char class[LTTNG_BUFFER_SZ];
+
+ enif_get_tuple(env, argv[4], &arity, &tuple);
+ erts_snprintf(class, LTTNG_BUFFER_SZ, "%T", tuple[0]);
+
+ if (enif_get_tuple(env, argv[3], &arity, &tuple)) {
+ enif_get_uint(env, tuple[2], &len);
+ lttng_mfa_to_str(tuple[0], tuple[1], len, mfa);
+ LTTNG3(function_exception, pid, mfa, class);
+ } else {
+ LTTNG3(function_exception, pid, undef, class);
+ }
+ }
+ return atom_ok;
+}
+
+static ERL_NIF_TERM enabled_send(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ ASSERT(argc == 3);
+ if (LTTNG_ENABLED(message_send))
+ return atom_trace;
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ lttng_decl_procbuf(pid);
+ lttng_pid_to_str(argv[2], pid);
+
+ if (argv[0] == atom_send) {
+ lttng_decl_procbuf(to);
+ char msg[LTTNG_BUFFER_SZ];
+
+ lttng_pid_to_str(argv[4], to);
+ erts_snprintf(msg, LTTNG_BUFFER_SZ, "%T", argv[3]);
+
+ LTTNG3(message_send, pid, to, msg);
+ } else if (argv[0] == atom_send_to_non_existing_process) {
+ lttng_decl_procbuf(to);
+ char msg[LTTNG_BUFFER_SZ];
+
+ lttng_pid_to_str(argv[4], to);
+ erts_snprintf(msg, LTTNG_BUFFER_SZ, "%T", argv[3]);
+ /* mark it as non existing ? */
+
+ LTTNG3(message_send, pid, to, msg);
+ }
+ return atom_ok;
+}
+
+static ERL_NIF_TERM enabled_receive(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ if (LTTNG_ENABLED(message_receive))
+ return atom_trace;
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_receive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ if (argv[0] == atom_receive) {
+ lttng_decl_procbuf(pid);
+ char msg[LTTNG_BUFFER_SZ];
+
+ lttng_pid_to_str(argv[2], pid);
+ erts_snprintf(msg, LTTNG_BUFFER_SZ, "%T", argv[3]);
+
+ LTTNG2(message_receive, pid, msg);
+ }
+ return atom_ok;
+}
+
+static ERL_NIF_TERM enabled_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ ASSERT(argc == 3);
+
+ if (argv[0] == atom_spawn && LTTNG_ENABLED(process_spawn)) {
+ return atom_trace;
+ } else if (argv[0] == atom_register && LTTNG_ENABLED(process_register)) {
+ return atom_trace;
+ } else if (argv[0] == atom_unregister && LTTNG_ENABLED(process_register)) {
+ return atom_trace;
+ } else if (argv[0] == atom_link && LTTNG_ENABLED(process_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_unlink && LTTNG_ENABLED(process_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_getting_linked && LTTNG_ENABLED(process_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_getting_unlinked && LTTNG_ENABLED(process_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_exit && LTTNG_ENABLED(process_exit)) {
+ return atom_trace;
+ }
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ lttng_decl_procbuf(pid);
+ lttng_decl_procbuf(to);
+
+ lttng_pid_to_str(argv[2], pid);
+
+ /* spawn */
+ if (argv[0] == atom_spawn) {
+ char undef[] = "undefined";
+ const ERL_NIF_TERM* tuple;
+ int arity;
+ unsigned int len;
+ lttng_decl_mfabuf(mfa);
+
+ lttng_pid_to_str(argv[3], to);
+
+ if (enif_get_tuple(env, argv[4], &arity, &tuple)) {
+ if (enif_is_list(env, tuple[2])) {
+ enif_get_list_length(env, tuple[2], &len);
+ } else {
+ enif_get_uint(env, tuple[2], &len);
+ }
+ lttng_mfa_to_str(tuple[0], tuple[1], len, mfa);
+ LTTNG3(process_spawn, to, pid, mfa);
+ } else {
+ LTTNG3(process_spawn, to, pid, undef);
+ }
+
+ /* register */
+ } else if (argv[0] == atom_register) {
+ char name[LTTNG_BUFFER_SZ];
+ erts_snprintf(name, LTTNG_BUFFER_SZ, "%T", argv[3]);
+ LTTNG3(process_register, pid, name, "register");
+ } else if (argv[0] == atom_unregister) {
+ char name[LTTNG_BUFFER_SZ];
+ erts_snprintf(name, LTTNG_BUFFER_SZ, "%T", argv[3]);
+ LTTNG3(process_register, pid, name, "unregister");
+ /* link */
+ } else if (argv[0] == atom_link) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(process_link, pid, to, "link");
+ } else if (argv[0] == atom_unlink) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(process_link, pid, to, "unlink");
+ } else if (argv[0] == atom_getting_linked) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(process_link, to, pid, "link");
+ } else if (argv[0] == atom_getting_unlinked) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(process_link, to, pid, "unlink");
+ /* exit */
+ } else if (argv[0] == atom_exit) {
+ char reason[LTTNG_BUFFER_SZ];
+ erts_snprintf(reason, LTTNG_BUFFER_SZ, "%T", argv[3]);
+ LTTNG2(process_exit, pid, reason);
+ }
+ return atom_ok;
+}
+
+static ERL_NIF_TERM enabled_ports(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ ASSERT(argc == 3);
+
+ if (argv[0] == atom_open && LTTNG_ENABLED(port_open)) {
+ return atom_trace;
+ } else if (argv[0] == atom_link && LTTNG_ENABLED(port_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_unlink && LTTNG_ENABLED(port_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_getting_linked && LTTNG_ENABLED(port_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_getting_unlinked && LTTNG_ENABLED(port_link)) {
+ return atom_trace;
+ } else if (argv[0] == atom_closed && LTTNG_ENABLED(port_exit)) {
+ return atom_trace;
+ }
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ lttng_decl_portbuf(port);
+ lttng_decl_procbuf(to);
+
+ lttng_portid_to_str(argv[2], port);
+
+ /* open and closed */
+ if (argv[0] == atom_open) {
+ char driver[LTTNG_BUFFER_SZ];
+ lttng_decl_procbuf(pid);
+ lttng_pid_to_str(argv[3], pid);
+
+ erts_snprintf(driver, LTTNG_BUFFER_SZ, "%T", argv[4]);
+ LTTNG3(port_open, pid, driver, port);
+ } else if (argv[0] == atom_closed) {
+ char reason[LTTNG_BUFFER_SZ];
+ erts_snprintf(reason, LTTNG_BUFFER_SZ, "%T", argv[3]);
+
+ LTTNG2(port_exit, port, reason);
+ /* link */
+ } else if (argv[0] == atom_link) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(port_link, port, to, "link");
+ } else if (argv[0] == atom_unlink) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(port_link, port, to, "unlink");
+ } else if (argv[0] == atom_getting_linked) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(port_link, to, port, "link");
+ } else if (argv[0] == atom_getting_unlinked) {
+ lttng_pid_to_str(argv[3], to);
+ LTTNG3(port_link, to, port, "unlink");
+ }
+ return atom_ok;
+}
+
+static ERL_NIF_TERM enabled_running_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ ASSERT(argc == 3);
+
+ if (LTTNG_ENABLED(process_scheduled))
+ return atom_trace;
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_running_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ lttng_decl_procbuf(pid);
+ const ERL_NIF_TERM* tuple;
+ char *mfastr = "undefined";
+ int arity;
+ lttng_decl_mfabuf(mfa);
+
+ lttng_pid_to_str(argv[2], pid);
+
+ if (enif_get_tuple(env, argv[3], &arity, &tuple)) {
+ int val;
+ enif_get_int(env, tuple[2], &val);
+ lttng_mfa_to_str(tuple[0], tuple[1], val, mfa);
+ mfastr = mfa;
+ }
+ /* running processes */
+ if (argv[0] == atom_in) {
+ LTTNG3(process_scheduled, pid, mfastr, "in");
+ } else if (argv[0] == atom_out) {
+ LTTNG3(process_scheduled, pid, mfastr, "out");
+ /* exiting */
+ } else if (argv[0] == atom_in_exiting) {
+ LTTNG3(process_scheduled, pid, mfastr, "in_exiting");
+ } else if (argv[0] == atom_out_exiting) {
+ LTTNG3(process_scheduled, pid, mfastr, "out_exiting");
+ } else if (argv[0] == atom_out_exited) {
+ LTTNG3(process_scheduled, pid, mfastr, "out_exited");
+ }
+
+ return atom_ok;
+}
+
+static ERL_NIF_TERM enabled_running_ports(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ ASSERT(argc == 3);
+
+ if (LTTNG_ENABLED(port_scheduled))
+ return atom_trace;
+
+ return atom_discard;
+}
+
+static ERL_NIF_TERM trace_running_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ lttng_decl_procbuf(pid);
+ lttng_decl_mfabuf(where);
+
+ lttng_portid_to_str(argv[2], pid);
+ erts_snprintf(where, LTTNG_BUFFER_SZ, "%T", argv[3]);
+
+ /* running ports */
+ if (argv[0] == atom_in) {
+ LTTNG3(port_scheduled, pid, where, "in");
+ } else if (argv[0] == atom_out) {
+ LTTNG3(port_scheduled, pid, where, "out");
+ /* exiting */
+ } else if (argv[0] == atom_in_exiting) {
+ LTTNG3(port_scheduled, pid, where, "in_exiting");
+ } else if (argv[0] == atom_out_exiting) {
+ LTTNG3(port_scheduled, pid, where, "out_exiting");
+ } else if (argv[0] == atom_out_exited) {
+ LTTNG3(port_scheduled, pid, where, "out_exited");
+ }
+ return atom_ok;
+}
+#endif
diff --git a/lib/runtime_tools/c_src/dyntrace_lttng.h b/lib/runtime_tools/c_src/dyntrace_lttng.h
new file mode 100644
index 0000000000..3550a1cab5
--- /dev/null
+++ b/lib/runtime_tools/c_src/dyntrace_lttng.h
@@ -0,0 +1,367 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER com_ericsson_dyntrace
+
+#if !defined(DYNTRACE_LTTNG_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define DYNTRACE_LTTNG_H
+
+#include <lttng/tracepoint.h>
+
+#define LTTNG1(Name, Arg1) \
+ tracepoint(com_ericsson_dyntrace, Name, (Arg1))
+
+#define LTTNG2(Name, Arg1, Arg2) \
+ tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2))
+
+#define LTTNG3(Name, Arg1, Arg2, Arg3) \
+ tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3))
+
+#define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \
+ tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4))
+
+#define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \
+ tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5))
+
+#define LTTNG_ENABLED(Name) \
+ tracepoint_enabled(com_ericsson_dyntrace, Name)
+
+#define LTTNG_BUFFER_SZ (256)
+#define LTTNG_PROC_BUFFER_SZ (16)
+#define LTTNG_PORT_BUFFER_SZ (20)
+#define LTTNG_MFA_BUFFER_SZ (256)
+
+#define lttng_decl_procbuf(Name) \
+ char Name[LTTNG_PROC_BUFFER_SZ]
+
+#define lttng_decl_portbuf(Name) \
+ char Name[LTTNG_PORT_BUFFER_SZ]
+
+#define lttng_decl_mfabuf(Name) \
+ char Name[LTTNG_MFA_BUFFER_SZ]
+
+#define lttng_pid_to_str(pid, name) \
+ erts_snprintf(name, LTTNG_PROC_BUFFER_SZ, "%T", (pid))
+
+#define lttng_portid_to_str(pid, name) \
+ erts_snprintf(name, LTTNG_PORT_BUFFER_SZ, "%T", (pid))
+
+#define lttng_proc_to_str(p, name) \
+ lttng_pid_to_str(((p) ? (p)->common.id : ERTS_INVALID_PID), name)
+
+#define lttng_port_to_str(p, name) \
+ lttng_portid_to_str(((p) ? (p)->common.id : ERTS_INVALID_PORT), name)
+
+#define lttng_mfa_to_str(m,f,a, Name) \
+ erts_snprintf(Name, LTTNG_MFA_BUFFER_SZ, "%T:%T/%lu", (Eterm)(m), (Eterm)(f), (Uint)(a))
+
+/* Process scheduling */
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ process_spawn,
+ TP_ARGS(
+ char*, p,
+ char*, parent,
+ char*, mfa
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_string(parent, parent)
+ ctf_string(entry, mfa)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ process_link,
+ TP_ARGS(
+ char*, from,
+ char*, to,
+ char*, type
+ ),
+ TP_FIELDS(
+ ctf_string(from, from)
+ ctf_string(to, to)
+ ctf_string(type, type)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ process_exit,
+ TP_ARGS(
+ char*, p,
+ char*, reason
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_string(reason, reason)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ process_register,
+ TP_ARGS(
+ char*, pid,
+ char*, name,
+ char*, type
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(name, name)
+ ctf_string(type, type)
+ )
+)
+
+/* Scheduled */
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ process_scheduled,
+ TP_ARGS(
+ char*, p,
+ char*, mfa,
+ char*, type
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_string(entry, mfa)
+ ctf_string(type, type)
+ )
+)
+
+/* Ports */
+
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ port_open,
+ TP_ARGS(
+ char*, pid,
+ char*, driver,
+ char*, port
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(driver, driver)
+ ctf_string(port, port)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ port_exit,
+ TP_ARGS(
+ char*, port,
+ char*, reason
+ ),
+ TP_FIELDS(
+ ctf_string(port, port)
+ ctf_string(reason, reason)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ port_link,
+ TP_ARGS(
+ char*, from,
+ char*, to,
+ char*, type
+ ),
+ TP_FIELDS(
+ ctf_string(from, from)
+ ctf_string(to, to)
+ ctf_string(type, type)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ port_scheduled,
+ TP_ARGS(
+ char*, p,
+ char*, op,
+ char*, type
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_string(entry, op)
+ ctf_string(type, type)
+ )
+)
+
+/* Call tracing */
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ function_call,
+ TP_ARGS(
+ char*, pid,
+ char*, mfa,
+ unsigned int, depth
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(entry, mfa)
+ ctf_integer(unsigned int, depth, depth)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ function_return,
+ TP_ARGS(
+ char*, pid,
+ char*, mfa,
+ unsigned int, depth
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(entry, mfa)
+ ctf_integer(unsigned int, depth, depth)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ function_exception,
+ TP_ARGS(
+ char*, pid,
+ char*, mfa,
+ char*, type
+ ),
+ TP_FIELDS(
+ ctf_string(pid, pid)
+ ctf_string(entry, mfa)
+ ctf_string(class, type)
+ )
+)
+
+/* Process messages */
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ message_send,
+ TP_ARGS(
+ char*, sender,
+ char*, receiver,
+ char*, msg
+ ),
+ TP_FIELDS(
+ ctf_string(from, sender)
+ ctf_string(to, receiver)
+ ctf_string(message, msg)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ message_receive,
+ TP_ARGS(
+ char*, receiver,
+ char*, msg
+ ),
+ TP_FIELDS(
+ ctf_string(to, receiver)
+ ctf_string(message, msg)
+ )
+)
+
+/* Process Memory */
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ gc_minor_start,
+ TP_ARGS(
+ char*, p,
+ unsigned long, need,
+ unsigned long, nh,
+ unsigned long, oh
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_integer(unsigned long, need, need)
+ ctf_integer(unsigned long, heap, nh)
+ ctf_integer(unsigned long, old_heap, oh)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ gc_minor_end,
+ TP_ARGS(
+ char*, p,
+ unsigned long, reclaimed,
+ unsigned long, nh,
+ unsigned long, oh
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_integer(unsigned long, reclaimed, reclaimed)
+ ctf_integer(unsigned long, heap, nh)
+ ctf_integer(unsigned long, old_heap, oh)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ gc_major_start,
+ TP_ARGS(
+ char*, p,
+ unsigned long, need,
+ unsigned long, nh,
+ unsigned long, oh
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_integer(unsigned long, need, need)
+ ctf_integer(unsigned long, heap, nh)
+ ctf_integer(unsigned long, old_heap, oh)
+ )
+)
+
+TRACEPOINT_EVENT(
+ com_ericsson_dyntrace,
+ gc_major_end,
+ TP_ARGS(
+ char*, p,
+ unsigned long, reclaimed,
+ unsigned long, nh,
+ unsigned long, oh
+ ),
+ TP_FIELDS(
+ ctf_string(pid, p)
+ ctf_integer(unsigned long, reclaimed, reclaimed)
+ ctf_integer(unsigned long, heap, nh)
+ ctf_integer(unsigned long, old_heap, oh)
+ )
+)
+
+#endif /* DYNTRACE_LTTNG_H */
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./dyntrace_lttng.h"
+
+/* This part must be outside protection */
+#include <lttng/tracepoint-event.h>
diff --git a/lib/runtime_tools/doc/src/LTTng.xml b/lib/runtime_tools/doc/src/LTTng.xml
new file mode 100644
index 0000000000..4a87133c57
--- /dev/null
+++ b/lib/runtime_tools/doc/src/LTTng.xml
@@ -0,0 +1,461 @@
+<?xml version="1.0" encoding="utf8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+<chapter>
+ <header>
+ <copyright>
+ <year>2016</year><year>2016</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </legalnotice>
+
+ <title>LTTng and Erlang/OTP</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2016-04-27</date>
+ <rev></rev>
+ <file>LTTng.xml</file>
+ </header>
+
+ <section>
+ <title>Introduction</title>
+ <p>The Linux Trace Toolkit: next generation is an open source system software package
+ for correlated tracing of the Linux kernel, user applications and libraries. </p>
+ <p>For more information, please visit <url href="http://lttng.org">http://lttng.org</url></p>
+ </section>
+
+ <section>
+ <title>Building Erlang/OTP with LTTng support</title>
+ <p>
+ Configure and build Erlang with LTTng support:
+ </p>
+ <p>For LTTng to work properly with Erlang/OTP you need
+ the following packages installed:</p>
+
+ <list type="bulleted">
+ <item><p>LTTng-tools: a command line interface to control tracing sessions.</p></item>
+ <item><p>LTTng-UST: user space tracing library.</p></item>
+ </list>
+
+ <p>On Ubuntu this can be installed via <c>aptitude</c>:</p>
+
+ <code type="none">$ sudo aptitude install lttng-tools liblttng-ust-dev</code>
+ <p>See <url href="http://lttng.org/docs/#doc-installing-lttng">Installing LTTng</url>
+ for more information on how to install LTTng on your system.</p>
+
+ <p>After LTTng is properly installed on the system Erlang/OTP can be built with LTTng support.</p>
+
+
+<code type="none">$ ./configure --with-dynamic-trace=lttng
+$ make </code>
+ </section>
+
+ <section>
+ <title>Dyntrace Tracepoints</title>
+ <p>All tracepoints are in the domain of <c>com_ericsson_dyntrace</c></p>
+ <p>All Erlang types are the string equivalent in LTTng.</p>
+
+ <p><em>process_spawn</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>parent : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">process_spawn: { cpu_id = 3 }, { pid = "&lt;0.131.0&gt;", parent = "&lt;0.130.0&gt;", entry = "erlang:apply/2" }</code></p>
+
+ <p><em>process_link</em></p>
+ <list type="bulleted">
+ <item><c>to : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>from : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>type : string</c> :: <c>"link" | "unlink"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">process_link: { cpu_id = 3 }, { from = "&lt;0.130.0&gt;", to = "&lt;0.131.0&gt;", type = "link" }</code></p>
+
+
+ <p><em>process_exit</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>reason : string</c> :: Exit reason. Ex. <c>"normal"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">process_exit: { cpu_id = 3 }, { pid = "&lt;0.130.0&gt;", reason = "normal" }</code></p>
+
+ <p><em>process_register</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>name : string</c> :: Registered name. Ex. <c>"error_logger"</c></item>
+ <item><c>type : string</c> :: <c>"register" | "unregister"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">process_register: { cpu_id = 0 }, { pid = "&lt;0.128.0&gt;", name = "dyntrace_lttng_SUITE" type = "register" }</code></p>
+
+ <p><em>process_scheduled</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
+ <item><c>type : string</c> :: <c>"in" | "out" | "in_exiting" | "out_exiting" | "out_exited"</c></item>
+ </list>
+
+ <p>Example:</p>
+ <p><code type="none">process_scheduled: { cpu_id = 0 }, { pid = "&lt;0.136.0&gt;", entry = "erlang:apply/2", type = "in" }</code></p>
+
+
+ <p><em>port_open</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ </list>
+
+ <p>Example:</p>
+ <p><code type="none">port_open: { cpu_id = 5 }, { pid = "&lt;0.131.0&gt;", driver = "'/bin/sh -s unix:cmd'", port = "#Port&lt;0.1887&gt;" }</code></p>
+
+ <p><em>port_exit</em></p>
+ <list type="bulleted">
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>reason : string</c> :: Exit reason. Ex. <c>"normal"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">port_exit: { cpu_id = 5 }, { port = "#Port&lt;0.1887&gt;", reason = "normal" }</code></p>
+
+ <p><em>port_link</em></p>
+ <list type="bulleted">
+ <item><c>to : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>from : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>type : string</c> :: <c>"link" | "unlink"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">port_link: { cpu_id = 5 }, { from = "#Port&lt;0.1887&gt;", to = "&lt;0.131.0&gt;", type = "unlink" }</code></p>
+
+ <p><em>port_scheduled</em></p>
+ <list type="bulleted">
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>entry : string</c> :: Callback. Ex. <c>"open"</c></item>
+ <item><c>type : string</c> :: <c>"in" | "out" | "in_exiting" | "out_exiting" | "out_exited"</c></item>
+ </list>
+
+ <p>Example:</p>
+ <p><code type="none">port_scheduled: { cpu_id = 5 }, { pid = "#Port&lt;0.1905&gt;", entry = "close", type = "out" }</code></p>
+
+ <p><em>function_call</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
+ <item><c>depth : integer</c> :: Stack depth. Ex. <c>0</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">function_call: { cpu_id = 5 }, { pid = "&lt;0.145.0&gt;", entry = "dyntrace_lttng_SUITE:'-t_call/1-fun-1-'/0", depth = 0 }</code></p>
+
+ <p><em>function_return</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
+ <item><c>depth : integer</c> :: Stack depth. Ex. <c>0</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">function_return: { cpu_id = 5 }, { pid = "&lt;0.145.0&gt;", entry = "dyntrace_lttng_SUITE:waiter/0", depth = 0 }</code></p>
+
+ <p><em>function_exception</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
+ <item><c>class : string</c> :: Error reason. Ex. <c>"error"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">function_exception: { cpu_id = 5 }, { pid = "&lt;0.144.0&gt;", entry = "t:call_exc/1", class = "error" }</code></p>
+
+ <p><em>message_send</em></p>
+ <list type="bulleted">
+ <item><c>from : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>to : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>message : string</c> :: Message sent. Ex. <c>"{&lt;0.162.0&gt;,ok}"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">message_send: { cpu_id = 3 }, { from = "#Port&lt;0.1938&gt;", to = "&lt;0.160.0&gt;", message = "{#Port&lt;0.1938&gt;,eof}" }</code></p>
+
+ <p><em>message_receive</em></p>
+ <list type="bulleted">
+ <item><c>to : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>message : string</c> :: Message received. Ex. <c>"{&lt;0.162.0&gt;,ok}"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">message_receive: { cpu_id = 7 }, { to = "&lt;0.167.0&gt;", message = "{&lt;0.165.0&gt;,ok}" }</code></p>
+
+ <p><em>gc_minor_start</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>need : integer</c> :: Heap need. Ex. <c>2</c></item>
+ <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
+ <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">gc_minor_start: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", need = 0, heap = 610, old_heap = 0 }</code></p>
+
+ <p><em>gc_minor_end</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>reclaimed : integer</c> :: Heap reclaimed. Ex. <c>2</c></item>
+ <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
+ <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">gc_minor_end: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", reclaimed = 120, heap = 1598, old_heap = 1598 }</code></p>
+
+ <p><em>gc_major_start</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>need : integer</c> :: Heap need. Ex. <c>2</c></item>
+ <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
+ <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">gc_major_start: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", need = 8, heap = 2586, old_heap = 1598 }</code></p>
+
+ <p><em>gc_major_end</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>reclaimed : integer</c> :: Heap reclaimed. Ex. <c>2</c></item>
+ <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
+ <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">gc_major_end: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", reclaimed = 240, heap = 4185, old_heap = 0 }</code></p>
+
+ </section>
+
+ <section>
+ <title>BEAM Tracepoints</title>
+ <p>All tracepoints are in the domain of <c>com_ericsson_otp</c></p>
+ <p>All Erlang types are the string equivalent in LTTng.</p>
+
+ <p><em>scheduler_poll</em></p>
+ <list type="bulleted">
+ <item><c>scheduler : integer</c> :: Scheduler ID. Ex. <c>1</c></item>
+ <item><c>runnable : integer</c> :: Runnable. Ex. <c>1</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">scheduler_poll: { cpu_id = 4 }, { scheduler = 1, runnable = 1 }</code></p>
+
+ <p><em>driver_init</em></p>
+ <list type="bulleted">
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ <item><c>major : integer</c> :: Major version. Ex. <c>3</c></item>
+ <item><c>minor : integer</c> :: Minor version. Ex. <c>1</c></item>
+ <item><c>flags : integer</c> :: Flags. Ex. <c>1</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_init: { cpu_id = 2 }, { driver = "caller_drv", major = 3, minor = 3, flags = 1 }</code></p>
+
+ <p><em>driver_start</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_start: { cpu_id = 2 }, { pid = "&lt;0.198.0&gt;", driver = "caller_drv", port = "#Port&lt;0.3676&gt;" }</code></p>
+
+ <p><em>driver_output</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ <item><c>bytes : integer</c> :: Size of data returned. Ex. <c>82</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_output: { cpu_id = 2 }, { pid = "&lt;0.198.0&gt;", port = "#Port&lt;0.3677&gt;", driver = "/bin/sh -s unix:cmd", bytes = 36 }</code></p>
+
+ <p><em>driver_outputv</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ <item><c>bytes : integer</c> :: Size of data returned. Ex. <c>82</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_outputv: { cpu_id = 5 }, { pid = "&lt;0.194.0&gt;", port = "#Port&lt;0.3663&gt;", driver = "tcp_inet", bytes = 3 }</code></p>
+
+ <p><em>driver_ready_input</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_ready_input: { cpu_id = 5 }, { pid = "&lt;0.189.0&gt;", port = "#Port&lt;0.3637&gt;", driver = "inet_gethost 4 " }</code></p>
+
+ <p><em>driver_ready_output</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_ready_output: { cpu_id = 5 }, { pid = "&lt;0.194.0&gt;", port = "#Port&lt;0.3663&gt;", driver = "tcp_inet" }</code></p>
+
+ <p><em>driver_timeout</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_timeout: { cpu_id = 5 }, { pid = "&lt;0.196.0&gt;", port = "#Port&lt;0.3664&gt;", driver = "tcp_inet" }</code></p>
+
+ <p><em>driver_stop_select</em></p>
+ <list type="bulleted">
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_stop_select: { cpu_id = 5 }, { driver = "unknown" }</code></p>
+
+ <p><em>driver_flush</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_flush: { cpu_id = 7 }, { pid = "&lt;0.204.0&gt;", port = "#Port&lt;0.3686&gt;", driver = "tcp_inet" }</code></p>
+
+ <p><em>driver_stop</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_stop: { cpu_id = 5 }, { pid = "[]", port = "#Port&lt;0.3673&gt;", driver = "efile" }</code></p>
+
+ <p><em>driver_process_exit</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+
+ <p><em>driver_ready_async</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_ready_async: { cpu_id = 3 }, { pid = "&lt;0.181.0&gt;", port = "#Port&lt;0.3622&gt;", driver = "efile" }</code></p>
+
+ <p><em>driver_call</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ <item><c>command : integer</c> :: Command integer. Ex. <c>1</c></item>
+ <item><c>bytes : integer</c> :: Size of data returned. Ex. <c>82</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_call: { cpu_id = 2 }, { pid = "&lt;0.202.0&gt;", port = "#Port&lt;0.3676&gt;", driver = "caller_drv", command = 0, bytes = 2 }</code></p>
+
+ <p><em>driver_control</em></p>
+ <list type="bulleted">
+ <item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
+ <item><c>command : integer</c> :: Command integer. Ex. <c>1</c></item>
+ <item><c>bytes : integer</c> :: Size of data returned. Ex. <c>82</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">driver_control: { cpu_id = 3 }, { pid = "&lt;0.32767.8191&gt;", port = "#Port&lt;0.0&gt;", driver = "forker", command = 83, bytes = 32 }</code></p>
+
+ <p><em>aio_pool_get</em></p>
+ <list type="bulleted">
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>length : integer</c> :: Async queue length. Ex. <c>0</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">aio_pool_get: { cpu_id = 4 }, { port = "#Port&lt;0.3614&gt;", length = 0 }</code></p>
+
+ <p><em>aio_pool_put</em></p>
+ <list type="bulleted">
+ <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
+ <item><c>length : integer</c> :: Async queue length. Ex. <c>-1</c></item>
+ </list>
+ <p>Async queue length is not defined for <c>put</c> operations.</p>
+ <p>Example:</p>
+ <p><code type="none">aio_pool_put: { cpu_id = 3 }, { port = "#Port&lt;0.3614&gt;", length = -1 }</code></p>
+
+ <p><em>carrier_create</em></p>
+ <list type="bulleted">
+ <item><c>type : string</c> :: Carrier type. Ex. <c>"ets_alloc"</c></item>
+ <item><c>instance : integer</c> :: Allocator instance. Ex. <c>1</c></item>
+ <item><c>size : integer</c> :: Carrier size. Ex. <c>262144</c></item>
+ <item><c>mbc_carriers : integer</c> :: Number of multiblock carriers in instance. Ex. <c>3</c></item>
+ <item><c>mbc_carriers_size : integer</c> :: Total size of multiblock blocks carriers in instance. Ex. <c>1343488</c></item>
+ <item><c>mbc_blocks : integer</c> :: Number of multiblock blocks in instance. Ex. <c>122</c></item>
+ <item><c>mbc_blocks_size : integer</c> :: Total size of all multiblock blocks in instance. Ex. <c>285296</c></item>
+ <item><c>sbc_carriers : integer</c> :: Number of singleblock carriers in instance. Ex. <c>1</c></item>
+ <item><c>sbc_carriers_size : integer</c> :: Total size of singleblock blocks carriers in instance. Ex. <c>1343488</c></item>
+ <item><c>sbc_blocks : integer</c> :: Number of singleblocks in instance. Ex. <c>1</c></item>
+ <item><c>sbc_blocks_size : integer</c> :: Total size of all singleblock blocks in instance. Ex. <c>285296</c></item>
+
+ </list>
+ <p>Example:</p>
+ <p><code type="none">carrier_create: { cpu_id = 2 }, { type = "ets_alloc", instance = 7, size = 2097152, mbc_carriers = 4, mbc_carriers_size = 3440640, mbc_blocks = 526, mbc_blocks_size = 1278576, sbc_carriers = 0, sbc_carriers_size = 0, sbc_blocks = 0, sbc_blocks_size = 0 }</code></p>
+
+ <p><em>carrier_destroy</em></p>
+ <list type="bulleted">
+ <item><c>type : string</c> :: Carrier type. Ex. <c>"ets_alloc"</c></item>
+ <item><c>instance : integer</c> :: Allocator instance. Ex. <c>1</c></item>
+ <item><c>size : integer</c> :: Carrier size. Ex. <c>262144</c></item>
+ <item><c>mbc_carriers : integer</c> :: Number of multiblock carriers in instance. Ex. <c>3</c></item>
+ <item><c>mbc_carriers_size : integer</c> :: Total size of multiblock blocks carriers in instance. Ex. <c>1343488</c></item>
+ <item><c>mbc_blocks : integer</c> :: Number of multiblock blocks in instance. Ex. <c>122</c></item>
+ <item><c>mbc_blocks_size : integer</c> :: Total size of all multiblock blocks in instance. Ex. <c>285296</c></item>
+ <item><c>sbc_carriers : integer</c> :: Number of singleblock carriers in instance. Ex. <c>1</c></item>
+ <item><c>sbc_carriers_size : integer</c> :: Total size of singleblock blocks carriers in instance. Ex. <c>1343488</c></item>
+ <item><c>sbc_blocks : integer</c> :: Number of singleblocks in instance. Ex. <c>1</c></item>
+ <item><c>sbc_blocks_size : integer</c> :: Total size of all singleblock blocks in instance. Ex. <c>285296</c></item>
+
+ </list>
+ <p>Example:</p>
+ <p><code type="none">carrier_destroy: { cpu_id = 6 }, { type = "ets_alloc", instance = 7, size = 262144, mbc_carriers = 3, mbc_carriers_size = 3178496, mbc_blocks = 925, mbc_blocks_size = 2305336, sbc_carriers = 0, sbc_carriers_size = 0, sbc_blocks = 0, sbc_blocks_size = 0 }</code></p>
+
+ <p><em>carrier_pool_put</em></p>
+ <list type="bulleted">
+ <item><c>type : string</c> :: Carrier type. Ex. <c>"ets_alloc"</c></item>
+ <item><c>instance : integer</c> :: Allocator instance. Ex. <c>1</c></item>
+ <item><c>size : integer</c> :: Carrier size. Ex. <c>262144</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">carrier_pool_put: { cpu_id = 3 }, { type = "ets_alloc", instance = 5, size = 1048576 }</code></p>
+
+ <p><em>carrier_pool_get</em></p>
+ <list type="bulleted">
+ <item><c>type : string</c> :: Carrier type. Ex. <c>"ets_alloc"</c></item>
+ <item><c>instance : integer</c> :: Allocator instance. Ex. <c>1</c></item>
+ <item><c>size : integer</c> :: Carrier size. Ex. <c>262144</c></item>
+ </list>
+ <p>Example:</p>
+ <p><code type="none">carrier_pool_get: { cpu_id = 7 }, { type = "ets_alloc", instance = 4, size = 3208 }</code></p>
+
+ </section>
+
+
+ <section>
+ <title>Examples</title>
+ </section>
+</chapter>
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index 0a590ff9ec..5ce40bb995 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -45,7 +45,7 @@ XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml system_information.x
XML_REF6_FILES = runtime_tools_app.xml
XML_PART_FILES = part_notes.xml part_notes_history.xml part.xml
-XML_CHAPTER_FILES = notes.xml notes_history.xml
+XML_CHAPTER_FILES = notes.xml notes_history.xml LTTng.xml
GENERATED_XML_FILES = DTRACE.xml SYSTEMTAP.xml
diff --git a/lib/runtime_tools/doc/src/part.xml b/lib/runtime_tools/doc/src/part.xml
index 14e8b71c83..34acf69fc8 100644
--- a/lib/runtime_tools/doc/src/part.xml
+++ b/lib/runtime_tools/doc/src/part.xml
@@ -34,6 +34,7 @@
<p><em>Runtime Tools</em></p>
</description>
+ <xi:include href="LTTng.xml"/>
<xi:include href="DTRACE.xml"/>
<xi:include href="SYSTEMTAP.xml"/>
</part>
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index d5ff874206..1620f52789 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -20,6 +20,7 @@
-module(dbg).
-export([p/1,p/2,c/3,c/4,i/0,start/0,stop/0,stop_clear/0,tracer/0,
tracer/2, tracer/3, get_tracer/0, get_tracer/1, tp/2, tp/3, tp/4,
+ tpe/2, ctpe/1,
ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, ctpl/0, ctpl/1,
ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3, ltp/0, wtp/1, rtp/1,
dtp/0, dtp/1, n/1, cn/1, ln/0, h/0, h/1]).
@@ -128,7 +129,12 @@ tpl(Module, Pattern) when is_atom(Module) ->
do_tp({Module, '_', '_'}, Pattern, [local]);
tpl({_Module, _Function, _Arity} = X, Pattern) ->
do_tp(X,Pattern,[local]).
-do_tp({_Module, _Function, _Arity} = X, Pattern, Flags)
+
+tpe(Event, Pattern) when Event =:= send;
+ Event =:= 'receive' ->
+ do_tp(Event, Pattern, []).
+
+do_tp(X, Pattern, Flags)
when is_integer(Pattern);
is_atom(Pattern) ->
case ets:lookup(get_pattern_table(), Pattern) of
@@ -137,17 +143,16 @@ do_tp({_Module, _Function, _Arity} = X, Pattern, Flags)
_ ->
{error, unknown_pattern}
end;
-do_tp({Module, _Function, _Arity} = X, Pattern, Flags) when is_list(Pattern) ->
+do_tp(X, Pattern, Flags) when is_list(Pattern) ->
Nodes = req(get_nodes),
- case Module of
- '_' ->
- ok;
- M when is_atom(M) ->
+ case X of
+ {M,_,_} when is_atom(M) ->
%% Try to load M on all nodes
lists:foreach(fun(Node) ->
rpc:call(Node, M, module_info, [])
end,
- Nodes)
+ Nodes);
+ _ -> ok
end,
case lint_tp(Pattern) of
{ok,_} ->
@@ -163,9 +168,9 @@ do_tp({Module, _Function, _Arity} = X, Pattern, Flags) when is_list(Pattern) ->
end.
%% All nodes are handled the same way - also the local node if it is traced
-do_tp_on_nodes(Nodes, MFA, P, Flags) ->
+do_tp_on_nodes(Nodes, X, P, Flags) ->
lists:map(fun(Node) ->
- case rpc:call(Node,erlang,trace_pattern,[MFA,P, Flags]) of
+ case rpc:call(Node,erlang,trace_pattern,[X,P, Flags]) of
N when is_integer(N) ->
{matched, Node, N};
Else ->
@@ -210,6 +215,7 @@ ctpg(Module) when is_atom(Module) ->
do_ctp({Module, '_', '_'}, [global]);
ctpg({_Module, _Function, _Arity} = X) ->
do_ctp(X,[global]).
+
do_ctp({Module, Function, Arity},[]) ->
do_ctp({Module, Function, Arity},[global]),
do_ctp({Module, Function, Arity},[local]);
@@ -217,6 +223,11 @@ do_ctp({_Module, _Function, _Arity}=MFA,Flags) ->
Nodes = req(get_nodes),
{ok,do_tp_on_nodes(Nodes,MFA,false,Flags)}.
+ctpe(Event) when Event =:= send;
+ Event =:= 'receive' ->
+ Nodes = req(get_nodes),
+ {ok,do_tp_on_nodes(Nodes,Event,true,[])}.
+
%%
%% ltp() -> ok
%% List saved and built-in trace patterns.
diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl
index f7dbef6929..28e6d67d96 100644
--- a/lib/runtime_tools/src/dyntrace.erl
+++ b/lib/runtime_tools/src/dyntrace.erl
@@ -41,6 +41,28 @@
pn/1, pn/2, pn/3, pn/4, pn/5, pn/6, pn/7, pn/8, pn/9]).
-export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]).
+-export([trace/5,
+ trace/6,
+ trace_procs/6,
+ trace_ports/6,
+ trace_running_procs/6,
+ trace_running_ports/6,
+ trace_call/6,
+ trace_send/6,
+ trace_receive/6,
+ trace_garbage_collection/6]).
+
+-export([enabled_procs/3,
+ enabled_ports/3,
+ enabled_running_procs/3,
+ enabled_running_ports/3,
+ enabled_call/3,
+ enabled_send/3,
+ enabled_receive/3,
+ enabled_garbage_collection/3,
+ enabled/3]).
+
+
-export([user_trace_i4s4/9]). % Know what you're doing!
-on_load(on_load/0).
@@ -125,6 +147,63 @@ user_trace_i4s4(_, _, _, _, _, _, _, _, _) ->
user_trace_n(_, _, _, _, _, _, _, _, _, _) ->
erlang:nif_error(nif_not_loaded).
+trace(_TracerState, _Label, _SeqTraceInfo, _, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_procs(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_ports(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_running_procs(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_running_ports(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_call(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_send(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_receive(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace_garbage_collection(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_procs(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_ports(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_running_procs(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_running_ports(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_call(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_send(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_receive(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
+enabled_garbage_collection(_TraceTag, _TracerState, _Tracee) ->
+ erlang:nif_error(nif_not_loaded).
+
%%%
%%% Erlang support functions
%%%
diff --git a/lib/runtime_tools/src/msacc.erl b/lib/runtime_tools/src/msacc.erl
index 612effa5aa..4db5dbec91 100644
--- a/lib/runtime_tools/src/msacc.erl
+++ b/lib/runtime_tools/src/msacc.erl
@@ -32,18 +32,18 @@
-type msacc_data() :: [msacc_data_thread()].
--type msacc_data_thread() :: #{ '$type' => msacc_data,
- type => msacc_type(), id => msacc_id(),
- counters => msacc_data_counters() }.
+-type msacc_data_thread() :: #{ '$type' := msacc_data,
+ type := msacc_type(), id := msacc_id(),
+ counters := msacc_data_counters() }.
-type msacc_data_counters() :: #{ msacc_state() => non_neg_integer()}.
-type msacc_stats() :: [msacc_stats_thread()].
--type msacc_stats_thread() :: #{ '$type' => msacc_stats,
- type => msacc_type(), id => msacc_id(),
- system => float(),
- counters => msacc_stats_counters()}.
--type msacc_stats_counters() :: #{ msacc_state() => #{ thread => float(),
- system => float()}}.
+-type msacc_stats_thread() :: #{ '$type' := msacc_stats,
+ type := msacc_type(), id := msacc_id(),
+ system := float(),
+ counters := msacc_stats_counters()}.
+-type msacc_stats_counters() :: #{ msacc_state() => #{ thread := float(),
+ system := float()}}.
-type msacc_type() :: scheduler | aux | async.
diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src
index 4d96996ce0..690c61a4c3 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -28,7 +28,7 @@
{applications, [kernel, stdlib]},
{env, []},
{mod, {runtime_tools, []}},
- {runtime_dependencies, ["stdlib-2.0","mnesia-4.12","kernel-3.0",
- "erts-7.0"]}]}.
+ {runtime_dependencies, ["stdlib-3.0","mnesia-4.12","kernel-5.0",
+ "erts-8.0"]}]}.
diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl
index ad7ee7311c..1add01612d 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -28,31 +28,25 @@
-behaviour(gen_server).
%% API
--export([
- report/0,
+-export([report/0,
from_file/1,
- to_file/1
- ]).
--export([
- start/0, stop/0,
- load_report/0, load_report/2,
- applications/0, applications/1,
- application/1, application/2,
- environment/0, environment/1,
- module/1, module/2,
- modules/1,
- sanity_check/0
- ]).
+ to_file/1]).
+-export([start/0, stop/0,
+ load_report/0, load_report/2,
+ applications/0, applications/1,
+ application/1, application/2,
+ environment/0, environment/1,
+ module/1, module/2,
+ modules/1,
+ sanity_check/0]).
%% gen_server callbacks
--export([
- init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3
- ]).
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
-define(SERVER, ?MODULE).
@@ -71,13 +65,13 @@ start() ->
gen_server:start({local, ?SERVER}, ?MODULE, [], []).
stop() ->
- gen_server:call(?SERVER, stop).
+ gen_server:call(?SERVER, stop, infinity).
load_report() -> load_report(data, report()).
load_report(file, File) -> load_report(data, from_file(File));
load_report(data, Report) ->
- start(), gen_server:call(?SERVER, {load_report, Report}).
+ start(), gen_server:call(?SERVER, {load_report, Report}, infinity).
report() -> [
{init_arguments, init:get_arguments()},
@@ -120,22 +114,22 @@ from_file(File) ->
applications() -> applications([]).
applications(Opts) when is_list(Opts) ->
- gen_server:call(?SERVER, {applications, Opts}).
+ gen_server:call(?SERVER, {applications, Opts}, infinity).
application(App) when is_atom(App) -> application(App, []).
application(App, Opts) when is_atom(App), is_list(Opts) ->
- gen_server:call(?SERVER, {application, App, Opts}).
+ gen_server:call(?SERVER, {application, App, Opts}, infinity).
environment() -> environment([]).
environment(Opts) when is_list(Opts) ->
- gen_server:call(?SERVER, {environment, Opts}).
+ gen_server:call(?SERVER, {environment, Opts}, infinity).
module(M) when is_atom(M) -> module(M, []).
module(M, Opts) when is_atom(M), is_list(Opts) ->
- gen_server:call(?SERVER, {module, M, Opts}).
+ gen_server:call(?SERVER, {module, M, Opts}, infinity).
modules(Opt) when is_atom(Opt) ->
- gen_server:call(?SERVER, {modules, Opt}).
+ gen_server:call(?SERVER, {modules, Opt}, infinity).
-spec sanity_check() -> ok | {failed, Failures} when
diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile
index 432a361468..61377ea09e 100644
--- a/lib/runtime_tools/test/Makefile
+++ b/lib/runtime_tools/test/Makefile
@@ -4,6 +4,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
dyntrace_SUITE \
+ dyntrace_lttng_SUITE \
runtime_tools_SUITE \
system_information_SUITE \
dbg_SUITE \
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index 17c04c0ed4..5a3c885571 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -20,316 +20,494 @@
-module(dbg_SUITE).
%% Test functions
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- big/1, tiny/1, simple/1, message/1, distributed/1, port/1,
- ip_port/1, file_port/1, file_port2/1, file_port_schedfix/1,
- ip_port_busy/1, wrap_port/1, wrap_port_time/1,
- with_seq_trace/1, dead_suspend/1, local_trace/1,
- saved_patterns/1, tracer_exit_on_stop/1,
+-export([all/0, suite/0,
+ big/1, tiny/1, simple/1, message/1, distributed/1, port/1,
+ send/1, recv/1,
+ ip_port/1, file_port/1, file_port2/1, file_port_schedfix/1,
+ ip_port_busy/1, wrap_port/1, wrap_port_time/1,
+ with_seq_trace/1, dead_suspend/1, local_trace/1,
+ saved_patterns/1, tracer_exit_on_stop/1,
erl_tracer/1, distributed_erl_tracer/1]).
--export([init_per_testcase/2, end_per_testcase/2]).
-export([tracee1/1, tracee2/1]).
-export([dummy/0, exported/1]).
-export([enabled/3, trace/6, load_nif/1]).
-include_lib("common_test/include/ct.hrl").
--define(default_timeout, ?t:minutes(1)).
-
-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.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[big, tiny, simple, message, distributed, port, ip_port,
+ send, recv,
file_port, file_port2, file_port_schedfix, ip_port_busy,
wrap_port, wrap_port_time, with_seq_trace, dead_suspend,
local_trace, saved_patterns, tracer_exit_on_stop,
erl_tracer, distributed_erl_tracer].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-big(suite) -> [];
-big(doc) -> ["Rudimentary interface test"];
+%% Rudimentary interface test
big(Config) when is_list(Config) ->
- ?line {ok,OldCurDir} = file:get_cwd(),
- Datadir=?config(data_dir, Config),
- Privdir=?config(priv_dir, Config),
- ?line ok=file:set_cwd(Privdir),
+ {ok,OldCurDir} = file:get_cwd(),
+ Datadir=proplists:get_value(data_dir, Config),
+ Privdir=proplists:get_value(priv_dir, Config),
+ ok=file:set_cwd(Privdir),
try
- %% make sure dbg is stopped (and returns correctly)
- ?line ok = dbg:stop(),
-
- %% compile test module and make sure it is loaded.
- ?line {ok,Mod} = compile:file(Datadir++"/dbg_test",[trace]),
- ?line code:purge(dbg_test),
- ?line {module, Mod}=code:load_file(dbg_test),
-
- %% run/debug a named test function.
- ?line Pid = spawn_link(dbg_test, loop, [Config]),
- ?line true = register(dbg_test_loop, Pid),
- ?line {ok,_} = dbg:tracer(),
- ?line {ok,[{matched, _node, 1}]} = dbg:p(dbg_test_loop, [m,p,c]),
- ?line ok = dbg:c(dbg_test, test, [Config]),
- ?line ok = dbg:i(),
- ?line dbg_test_loop ! {dbg_test, stop},
- unregister(dbg_test_loop),
- ?line ok = dbg:stop(),
-
- %% run/debug a Pid.
- ?line Pid2=spawn_link(dbg_test,loop,[Config]),
- ?line {ok,_} = dbg:tracer(),
- ?line {ok,[{matched, _node, 1}]} = dbg:p(Pid2,[s,r,p]),
- ?line ok = dbg:c(dbg_test, test, [Config]),
- ?line ok = dbg:i(),
- ?line Pid2 ! {dbg_test, stop},
-
- ?line ok=file:set_cwd(OldCurDir)
+ %% make sure dbg is stopped (and returns correctly)
+ ok = dbg:stop(),
+
+ %% compile test module and make sure it is loaded.
+ {ok,Mod} = compile:file(Datadir++"/dbg_test",[trace]),
+ code:purge(dbg_test),
+ {module, Mod}=code:load_file(dbg_test),
+
+ %% run/debug a named test function.
+ Pid = spawn_link(dbg_test, loop, [Config]),
+ true = register(dbg_test_loop, Pid),
+ {ok,_} = dbg:tracer(),
+ {ok,[{matched, _node, 1}]} = dbg:p(dbg_test_loop, [m,p,c]),
+ ok = dbg:c(dbg_test, test, [Config]),
+ ok = dbg:i(),
+ dbg_test_loop ! {dbg_test, stop},
+ unregister(dbg_test_loop),
+ ok = dbg:stop(),
+
+ %% run/debug a Pid.
+ Pid2=spawn_link(dbg_test,loop,[Config]),
+ {ok,_} = dbg:tracer(),
+ {ok,[{matched, _node, 1}]} = dbg:p(Pid2,[s,r,p]),
+ ok = dbg:c(dbg_test, test, [Config]),
+ ok = dbg:i(),
+ Pid2 ! {dbg_test, stop},
+
+ ok=file:set_cwd(OldCurDir)
after
- ?line dbg:stop()
+ dbg:stop()
end,
ok.
-tiny(suite) -> [];
-tiny(doc) -> ["Rudimentary interface test"];
+%% Rudimentary interface test
tiny(Config) when is_list(Config) ->
- ?line {ok,OldCurDir} = file:get_cwd(),
- Datadir=?config(data_dir, Config),
- Privdir=?config(priv_dir, Config),
- ?line ok=file:set_cwd(Privdir),
+ {ok,OldCurDir} = file:get_cwd(),
+ Datadir=proplists:get_value(data_dir, Config),
+ Privdir=proplists:get_value(priv_dir, Config),
+ ok=file:set_cwd(Privdir),
try
- %% compile test module and make sure it is loaded.
- ?line {ok, Mod} = compile:file(Datadir++"/dbg_test",[trace]),
- ?line code:purge(dbg_test),
- ?line {module, Mod}=code:load_file(dbg_test),
-
- ?line Pid=spawn_link(dbg_test,loop,[Config]),
- if
- is_pid(Pid) ->
- ?line dbg:tracer(),
- ?line {ok,[{matched, _node, 1}]} = dbg:p(Pid,[s,r,m,p,c]),
- ?line ok = dbg:c(dbg_test,test,[Config]),
- ?line ok = dbg:i(),
- ?line Pid ! {dbg_test, stop};
- true ->
- ?line ok=file:set_cwd(OldCurDir),
- ?t:fail("Could not spawn external test process.~n"),
- failure
- end
+ %% compile test module and make sure it is loaded.
+ {ok, Mod} = compile:file(Datadir++"/dbg_test",[trace]),
+ code:purge(dbg_test),
+ {module, Mod}=code:load_file(dbg_test),
+
+ Pid=spawn_link(dbg_test,loop,[Config]),
+ if
+ is_pid(Pid) ->
+ dbg:tracer(),
+ {ok,[{matched, _node, 1}]} = dbg:p(Pid,[s,r,m,p,c]),
+ ok = dbg:c(dbg_test,test,[Config]),
+ ok = dbg:i(),
+ Pid ! {dbg_test, stop};
+ true ->
+ ok=file:set_cwd(OldCurDir),
+ ct:fail("Could not spawn external test process.~n"),
+ failure
+ end
after
- ?line ok = dbg:stop(),
- ?line ok = file:set_cwd(OldCurDir)
+ ok = dbg:stop(),
+ ok = file:set_cwd(OldCurDir)
end,
ok.
-simple(suite) ->
- [];
-simple(doc) ->
- ["Simple interface test with own handler"];
+%% Simple interface test with own handler
simple(Config) when is_list(Config) ->
try
- ?line start(),
- ?line dbg:p(self(),call),
- ?line dbg:tp(dbg,ltp,[]),
- ?line dbg:ltp(),
- ?line stop(),
- ?line S = self(),
- ?line [{trace,S,call,{dbg,ltp,[]}}] = flush()
+ start(),
+ dbg:p(self(),call),
+ dbg:tp(dbg,ltp,[]),
+ dbg:ltp(),
+ stop(),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]}}] = flush()
after
- ?line dbg:stop()
+ dbg:stop()
end,
ok.
-message(suite) ->
- [];
-message(doc) ->
- ["Simple interface test with pam code that appends a message"];
+%% Simple interface test with pam code that appends a message
message(Config) when is_list(Config) ->
- ?line {ok, _} = start(),
+ {ok, _} = start(),
try
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line {ok, X} = dbg:tp(dbg,ltp,[{'_',[],[{message, {self}}]}]),
- ?line {value, {saved, Saved}} = lists:keysearch(saved, 1, X),
- ?line {ok, Y} = dbg:tp(dbg,ln,Saved),
- ?line {value, {saved, Saved}} = lists:keysearch(saved, 1, Y),
- ?line ok = dbg:ltp(),
- ?line ok = dbg:ln()
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, X} = dbg:tp(dbg,ltp,[{'_',[],[{message, {self}}]}]),
+ {value, {saved, Saved}} = lists:keysearch(saved, 1, X),
+ {ok, Y} = dbg:tp(dbg,ln,Saved),
+ {value, {saved, Saved}} = lists:keysearch(saved, 1, Y),
+ ok = dbg:ltp(),
+ ok = dbg:ln()
after
- ?line stop()
+ stop()
end,
- ?line S = self(),
- ?line [{trace,S,call,{dbg,ltp,[]},S},
- {trace,S,call,{dbg,ln,[]},S}] = flush(),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]},S},
+ {trace,S,call,{dbg,ln,[]},S}] = flush(),
ok.
-distributed(suite) ->
- [];
-distributed(doc) ->
- ["Simple test of distributed tracing"];
+send(Config) when is_list(Config) ->
+ {ok, _} = start(),
+ Node = start_slave(),
+ rpc:call(Node, code, add_patha,
+ [filename:join(proplists:get_value(data_dir, Config), "..")]),
+ try
+ Echo = fun F() ->
+ receive {From, M} ->
+ From ! M,
+ F()
+ end
+ end,
+ Rcvr = spawn_link(Echo),
+ RemoteRcvr = spawn_link(Node, Echo),
+
+ {ok, [{matched, _, 1}]} = dbg:p(Rcvr, send),
+
+ send_test(Rcvr, make_ref(), true),
+
+ %% Test that the test case process is the receiving process
+ send_test(Rcvr, [{[self(),'_'],[],[]}]),
+
+ %% Test that self() is not the receiving process
+ {ok, [{matched, _node, 1}, {saved, 2}]} =
+ dbg:tpe(send, [{['$1','_'],[{'==','$1',{self}}],[]}]),
+ send_test(Rcvr, make_ref(), false),
+
+ %% Test that self() is the sending process
+ send_test(Rcvr, [{['_','_'],[{'==',Rcvr,{self}}],[]}]),
+
+ %% Test attaching a message
+ send_test(Rcvr, [{['_','_'],[{'==',Rcvr,{self}}],[{message, hello}]}],
+ make_ref(), hello),
+
+ %% Test using a saved trace pattern
+ send_test(Rcvr, 2, make_ref(), false),
+
+ %% Test clearing of trace pattern
+ {ok, [{matched, _node, 1}]} = dbg:ctpe(send),
+ send_test(Rcvr, make_ref(), true),
+
+ %% Test complex message inspection
+ Ref = make_ref(),
+ send_test(Rcvr,
+ [{['_','$2'],[{'==',Ref,{element,1,{element,2,'$2'}}}],[]}],
+ {test, {Ref}, <<0:(8*1024)>>}, true),
+
+ send_test(Rcvr, {test, {make_ref()}, <<0:(8*1024)>>}, false),
+
+ %% Test send to remote process
+ remote_send_test(Rcvr, RemoteRcvr, [], make_ref(), true),
+
+ remote_send_test(Rcvr, RemoteRcvr,
+ [{['$1','_'],[{'==',{node, '$1'},{node}}],[]}],
+ make_ref(), false),
+
+ remote_send_test(Rcvr, RemoteRcvr,
+ [{['$1','_'],[{'==',{node, '$1'},Node}],[]}],
+ make_ref(), true),
+
+ %% Test that distributed dbg works
+ dbg:tracer(Node, process, {fun myhandler/2, self()}),
+ Rcvr2 = spawn_link(Echo),
+ RemoteRcvr2 = spawn_link(Node, Echo),
+ dbg:p(Rcvr2, [send]),
+ dbg:p(RemoteRcvr2, [send]),
+ dbg:tpe(send, [{['_', hej],[],[]}]),
+
+ send_test(Rcvr2, make_ref(), false),
+ send_test(RemoteRcvr2, make_ref(), false),
+ send_test(Rcvr2, hej, true),
+ send_test(RemoteRcvr2, hej, true),
+
+ ok
+
+ after
+ stop()
+ end.
+
+send_test(Pid, Pattern, Msg, TraceEvent) ->
+ {ok, [{matched, _, _}, _]} = dbg:tpe(send, Pattern),
+ send_test(Pid, Msg, TraceEvent).
+send_test(Pid, Pattern) ->
+ send_test(Pid, Pattern, make_ref(), true).
+send_test(Pid, Msg, TraceEvent) ->
+ S = self(),
+ Pid ! {S, Msg},
+ receive Msg -> ok end,
+ send_test_rcv(Pid, Msg, S, TraceEvent).
+
+remote_send_test(Pid, RPid, Pattern, Msg, TraceEvent) ->
+ dbg:tpe(send, Pattern),
+ TMsg = {self(), Msg},
+ Pid ! {RPid, TMsg},
+ receive Msg -> ok end,
+ send_test_rcv(Pid, TMsg, RPid, TraceEvent).
+
+send_test_rcv(Pid, Msg, S, TraceEvent) ->
+ case flush() of
+ [] when not TraceEvent ->
+ ok;
+ [{trace, Pid, send, Msg, S}] when TraceEvent ->
+ ok;
+ [{trace, Pid, send, Msg, S, Message}] when TraceEvent == Message ->
+ ok;
+ Else ->
+ ct:fail({got_unexpected_message, Else})
+ end.
+
+recv(Config) when is_list(Config) ->
+ {ok, _} = start(),
+ Node = start_slave(),
+ rpc:call(Node, code, add_patha,
+ [filename:join(proplists:get_value(data_dir, Config), "..")]),
+ try
+ Echo = fun F() ->
+ receive {From, M} ->
+ From ! M,
+ F()
+ end
+ end,
+ Rcvr = spawn_link(Echo),
+ RemoteRcvr = spawn_link(Node, Echo),
+
+ {ok, [{matched, _, 1}]} = dbg:p(Rcvr, 'receive'),
+
+ recv_test(Rcvr, make_ref(), true),
+
+ %% Test that the test case process is the sending process
+ recv_test(Rcvr, [{[node(), self(), '_'],[],[]}]),
+
+ %% Test that self() is the not sending process
+ {ok, [{matched, _node, 1}, {saved, 2}]} =
+ dbg:tpe('receive', [{[node(), '$1','_'],[{'==','$1',{self}}],[]}]),
+ recv_test(Rcvr, make_ref(), false),
+
+ %% Test that self() is the receiving process
+ recv_test(Rcvr, [{'_',[{'==',Rcvr,{self}}],[]}]),
+
+ %% Test attaching a message
+ recv_test(Rcvr, [{'_',[{'==',Rcvr,{self}}],[{message, hello}]}],
+ make_ref(), hello),
+
+ %% Test using a saved trace pattern
+ recv_test(Rcvr, 2, make_ref(), false),
+
+ %% Test clearing of trace pattern
+ {ok, [{matched, _node, 1}]} = dbg:ctpe('receive'),
+ recv_test(Rcvr, make_ref(), true),
+
+ %% Test complex message inspection
+ Ref = make_ref(),
+ recv_test(Rcvr,
+ [{[node(), '_','$2'],[{'==',Ref,{element,1,
+ {element,2,
+ {element,2,'$2'}}}}],[]}],
+ {test, {Ref}, <<0:(8*1024)>>}, true),
+
+ recv_test(Rcvr, {test, {make_ref()}, <<0:(8*1024)>>}, false),
+
+ %% Test recv to remote process
+ remote_recv_test(RemoteRcvr, Rcvr, [], make_ref(), true),
+
+ remote_recv_test(RemoteRcvr, Rcvr,
+ [{['$1',undefined,'_'],[{'==','$1',{node}}],[]}],
+ make_ref(), false),
+
+ remote_recv_test(RemoteRcvr, Rcvr,
+ [{['$1',undefined,'_'],[{'==','$1',Node}],[]}],
+ make_ref(), true),
+
+ %% Test that distributed dbg works
+ dbg:tracer(Node, process, {fun myhandler/2, self()}),
+ Rcvr2 = spawn_link(Echo),
+ RemoteRcvr2 = spawn_link(Node, Echo),
+ dbg:p(Rcvr2, ['receive']),
+ dbg:p(RemoteRcvr2, ['receive']),
+ dbg:tpe('receive', [{[node(), '_', '$1'],[{'==',{element,2,'$1'}, hej}],[]}]),
+
+ recv_test(Rcvr2, make_ref(), false),
+ recv_test(RemoteRcvr2, make_ref(), false),
+ recv_test(Rcvr2, hej, true),
+ recv_test(RemoteRcvr2, hej, true),
+
+ ok
+
+ after
+ stop()
+ end.
+
+recv_test(Pid, Pattern, Msg, TraceEvent) ->
+ {ok, [{matched, _, _}, _]} = dbg:tpe('receive', Pattern),
+ recv_test(Pid, Msg, TraceEvent).
+recv_test(Pid, Pattern) ->
+ recv_test(Pid, Pattern, make_ref(), true).
+recv_test(Pid, Msg, TraceEvent) ->
+ S = self(),
+ Pid ! {S, Msg},
+ receive Msg -> ok end,
+ recv_test_rcv(Pid, {S, Msg}, TraceEvent).
+
+remote_recv_test(RPid, Pid, Pattern, Msg, TraceEvent) ->
+ dbg:tpe('receive', Pattern),
+ TMsg = {self(), Msg},
+ RPid ! {Pid, TMsg},
+ receive Msg -> ok end,
+ recv_test_rcv(Pid, TMsg, TraceEvent).
+
+recv_test_rcv(Pid, Msg, TraceEvent) ->
+ case flush() of
+ [] when not TraceEvent ->
+ ok;
+ [{trace, Pid, 'receive', Msg}] when TraceEvent ->
+ ok;
+ [{trace, Pid, 'receive', Msg, Message}] when TraceEvent == Message ->
+ ok;
+ Else ->
+ ct:fail({got_unexpected_message, Else})
+ end.
+
+%% Simple test of distributed tracing
distributed(Config) when is_list(Config) ->
- ?line {ok, _} = start(),
- ?line Node = start_slave(),
+ {ok, _} = start(),
+ Node = start_slave(),
try
- ?line RexPid = rpc:call(Node, erlang, whereis, [rex]),
- ?line RexPidList = pid_to_list(RexPid),
- ?line {ok, Node} = dbg:n(Node),
- ?line {ok, X} = dbg:p(all,call),
- ?line {value, {matched, Node, _}} = lists:keysearch(Node, 2, X),
- ?line {ok, Y} = dbg:p(RexPidList, s),
- ?line {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Y),
- ?line {ok, Z} = dbg:tp(dbg,ltp,[]),
- ?line {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Z),
- ?line dbg:cn(Node),
- ?line dbg:tp(dbg,ln,[]),
- ?line ok = rpc:call(Node, dbg, ltp, []),
- ?line ok = rpc:call(Node, dbg, ln, []),
- ?line ok = dbg:ln(),
- ?line S = self(),
- ?line {TraceSend, TraceCall} =
- lists:partition(fun ({trace,RP,send,_,_}) when RP =:= RexPid -> true;
- (_) -> false end,
- flush()),
- ?line [_|_] = TraceSend,
- ?line [{trace,Pid,call,{dbg,ltp,[]}},
- {trace,S,call,{dbg,ln,[]}}] = TraceCall,
- ?line Node = node(Pid),
- %%
- ?line stop()
+ RexPid = rpc:call(Node, erlang, whereis, [rex]),
+ RexPidList = pid_to_list(RexPid),
+ {ok, Node} = dbg:n(Node),
+ {ok, X} = dbg:p(all,call),
+ {value, {matched, Node, _}} = lists:keysearch(Node, 2, X),
+ {ok, Y} = dbg:p(RexPidList, s),
+ {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Y),
+ {ok, Z} = dbg:tp(dbg,ltp,[]),
+ {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Z),
+ dbg:cn(Node),
+ dbg:tp(dbg,ln,[]),
+ ok = rpc:block_call(Node, dbg, ltp, []),
+ ok = rpc:block_call(Node, dbg, ln, []),
+ ok = dbg:ln(),
+ S = self(),
+ {TraceSend, TraceCall} =
+ lists:partition(fun ({trace,RP,send,_,_}) when RP =:= RexPid -> true;
+ (_) -> false end,
+ flush()),
+ [_|_] = TraceSend,
+ [{trace,Pid,call,{dbg,ltp,[]}},
+ {trace,S,call,{dbg,ln,[]}}] = TraceCall,
+ Node = node(Pid),
+ %%
+ stop()
after
- ?line stop_slave(Node),
- ?line stop()
+ stop_slave(Node),
+ stop()
end,
ok.
-local_trace(suite) ->
- [];
-local_trace(doc) ->
- ["Tests tracing of local function calls."];
+%% Tests tracing of local function calls.
local_trace(Config) when is_list(Config) ->
- ?line {ok, _} = start(),
+ {ok, _} = start(),
try
- ?line S = self(),
- ?line %% Split "<X.Y.Z>" into {X, Y, Z}
- ?line "<"++L1 = L = pid_to_list(S),
- ?line NoDot = fun ($.) -> false; (_) -> true end,
- ?line {LX,"."++L2} = lists:splitwith(NoDot, L1),
- ?line {LY,"."++L3} = lists:splitwith(NoDot, L2),
- ?line ">"++L4 = lists:reverse(L3),
- ?line LZ = lists:reverse(L4),
- ?line X = 0 = list_to_integer(LX),
- ?line Y = list_to_integer(LY),
- ?line Z = list_to_integer(LZ),
- ?line XYZ = {X, Y, Z},
- ?line io:format("Self = ~w = ~w~n", [S,XYZ]),
- ?line {ok, [{matched, _node, 1}]} = dbg:p(S,call),
- ?line {ok, [{matched, _node, 1}]} = dbg:p(XYZ,call),
- if Z =:= 0 ->
- ?line {ok, [{matched, _node, 1}]} = dbg:p(Y,call);
- true -> ok
- end,
- ?line {ok, [{matched, _node, 1}]} = dbg:p(L,call),
- ?line {ok, _} = dbg:tpl(?MODULE,not_exported,[]),
- ?line 4 = not_exported(2),
- ?line [{trace,S,call,{?MODULE,not_exported,[2]}}] = flush(),
- ?line {ok, _} = dbg:tp(?MODULE,exported,[]),
- ?line 4 = ?MODULE:exported(2),
- ?line [{trace,S,call,{?MODULE,exported,[2]}},
- {trace,S,call,{?MODULE,not_exported,[2]}}] = flush(),
- ?line {ok, _} = dbg:ctpl(?MODULE),
- ?line 4 = ?MODULE:exported(2),
- ?line [{trace,S,call,{?MODULE,exported,[2]}}] = flush(),
- ?line {ok, _} = dbg:tpl(?MODULE,not_exported,[]),
- ?line {ok, _} = dbg:ctp(?MODULE),
- ?line 4 = ?MODULE:exported(2),
- ?line [] = flush(),
- ?line {ok, _} = dbg:tpl(?MODULE,not_exported,x),
- ?line catch ?MODULE:exported(x),
- ?line [{trace,S,call,{dbg_SUITE,not_exported,[x]}},
- {trace,S,exception_from,
- {dbg_SUITE,not_exported,1},
- {error,badarith}}] = flush()
+ S = self(),
+ %% Split "<X.Y.Z>" into {X, Y, Z}
+ "<"++L1 = L = pid_to_list(S),
+ NoDot = fun ($.) -> false; (_) -> true end,
+ {LX,"."++L2} = lists:splitwith(NoDot, L1),
+ {LY,"."++L3} = lists:splitwith(NoDot, L2),
+ ">"++L4 = lists:reverse(L3),
+ LZ = lists:reverse(L4),
+ X = 0 = list_to_integer(LX),
+ Y = list_to_integer(LY),
+ Z = list_to_integer(LZ),
+ XYZ = {X, Y, Z},
+ io:format("Self = ~w = ~w~n", [S,XYZ]),
+ {ok, [{matched, _node, 1}]} = dbg:p(S,call),
+ {ok, [{matched, _node, 1}]} = dbg:p(XYZ,call),
+ if Z =:= 0 ->
+ {ok, [{matched, _node, 1}]} = dbg:p(Y,call);
+ true -> ok
+ end,
+ {ok, [{matched, _node, 1}]} = dbg:p(L,call),
+ {ok, _} = dbg:tpl(?MODULE,not_exported,[]),
+ 4 = not_exported(2),
+ [{trace,S,call,{?MODULE,not_exported,[2]}}] = flush(),
+ {ok, _} = dbg:tp(?MODULE,exported,[]),
+ 4 = ?MODULE:exported(2),
+ [{trace,S,call,{?MODULE,exported,[2]}},
+ {trace,S,call,{?MODULE,not_exported,[2]}}] = flush(),
+ {ok, _} = dbg:ctpl(?MODULE),
+ 4 = ?MODULE:exported(2),
+ [{trace,S,call,{?MODULE,exported,[2]}}] = flush(),
+ {ok, _} = dbg:tpl(?MODULE,not_exported,[]),
+ {ok, _} = dbg:ctp(?MODULE),
+ 4 = ?MODULE:exported(2),
+ [] = flush(),
+ {ok, _} = dbg:tpl(?MODULE,not_exported,x),
+ catch ?MODULE:exported(x),
+ [{trace,S,call,{dbg_SUITE,not_exported,[x]}},
+ {trace,S,exception_from,
+ {dbg_SUITE,not_exported,1},
+ {error,badarith}}] = flush()
after
- ?line stop()
+ stop()
end,
ok.
-port(suite) ->
- [];
-port(doc) ->
- ["Test that tracing on port works"];
+%% Test that tracing on port works
port(Config) when is_list(Config) ->
try
S = self(),
- start(),
+ start(),
TestFile = filename:join(proplists:get_value(priv_dir, Config),"port_test"),
Fun = dbg:trace_port(file, TestFile),
%% Do a run to get rid of all extra port operations
port_close(Fun()),
- dbg:p(new,ports),
+ dbg:p(new,ports),
Port = Fun(),
port_close(Port),
- stop(),
+ stop(),
TraceFileDrv = list_to_atom(lists:flatten(["trace_file_drv n ",TestFile])),
- [{trace,Port,open,S,TraceFileDrv},
+ [{trace,Port,open,S,TraceFileDrv},
{trace,Port,getting_linked,S},
{trace,Port,closed,normal},
{trace,Port,unlink,S}] = flush()
after
- dbg:stop()
+ dbg:stop()
end,
ok.
-saved_patterns(suite) ->
- [];
-saved_patterns(doc) ->
- ["Tests saving of match_spec's."];
+%% Tests saving of match_spec's.
saved_patterns(Config) when is_list(Config) ->
- ?line dbg:stop(),
- ?line {ok,[{saved,1}]} =
- dbg:tp(dbg,ctp,1,[{'_',[],[{message, blahonga}]}]),
- ?line {ok,[{saved,2}]} =
- dbg:tp(dbg,ctp,1,[{['_'],[],[{message, blahonga}]}]),
- ?line Privdir=?config(priv_dir, Config),
- ?line file:make_dir(Privdir),
- ?line File = filename:join([Privdir, "blahonga.ms"]),
- ?line dbg:wtp(File),
- ?line dbg:stop(),
- ?line dbg:ctp('_','_','_'),
- ?line {ok, _} = start(),
+ dbg:stop(),
+ {ok,[{saved,1}]} =
+ dbg:tp(dbg,ctp,1,[{'_',[],[{message, blahonga}]}]),
+ {ok,[{saved,2}]} =
+ dbg:tp(dbg,ctp,1,[{['_'],[],[{message, blahonga}]}]),
+ Privdir=proplists:get_value(priv_dir, Config),
+ file:make_dir(Privdir),
+ File = filename:join([Privdir, "blahonga.ms"]),
+ dbg:wtp(File),
+ dbg:stop(),
+ dbg:ctp('_','_','_'),
+ {ok, _} = start(),
try
- ?line dbg:rtp(File),
- ?line {ok,[{matched,_node,1},{saved,1}]} = dbg:tp(dbg,ltp,0,1),
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line dbg:ltp(),
- ?line S = self(),
- ?line [{trace,S,call,{dbg,ltp,[]},blahonga}] = flush()
+ dbg:rtp(File),
+ {ok,[{matched,_node,1},{saved,1}]} = dbg:tp(dbg,ltp,0,1),
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ dbg:ltp(),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]},blahonga}] = flush()
after
- ?line stop()
+ stop()
end,
ok.
@@ -341,148 +519,132 @@ not_exported(N) ->
exported(N) ->
not_exported(N).
-ip_port(suite) ->
- [];
-ip_port(doc) ->
- ["Test tracing to IP port"];
+%% Test tracing to IP port
ip_port(Config) when is_list(Config) ->
- ?line stop(),
- ?line Port = dbg:trace_port(ip, 0),
- ?line {ok, _} = dbg:tracer(port, Port),
+ stop(),
+ Port = dbg:trace_port(ip, 0),
+ {ok, _} = dbg:tracer(port, Port),
try
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
- ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
- ?line {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
- ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y),
- ?line ok = dbg:ltp(),
- ?line ok = dbg:ln(),
- ?line {ok, IpPort} = dbg:trace_port_control(get_listen_port),
- ?line io:format("IpPort = ~p~n", [IpPort]),
- ?line dbg:trace_client(ip, IpPort, {fun myhandler/2, self()}),
- ?line S = self(),
- ?line [{trace,S,call,{dbg,ltp,[]},S},
- {trace,S,call,{dbg,ln,[]},hej}] = flush()
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
+ {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ {value, {saved, _}} = lists:keysearch(saved, 1, Y),
+ ok = dbg:ltp(),
+ ok = dbg:ln(),
+ {ok, IpPort} = dbg:trace_port_control(get_listen_port),
+ io:format("IpPort = ~p~n", [IpPort]),
+ dbg:trace_client(ip, IpPort, {fun myhandler/2, self()}),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]},S},
+ {trace,S,call,{dbg,ln,[]},hej}] = flush()
after
- ?line stop()
+ stop()
end,
ok.
-ip_port_busy(suite) ->
- [];
-ip_port_busy(doc) ->
- ["Test that the dbg server does not hang if the tracer don't start ",
- "(OTP-3592)"];
+%% Test that the dbg server does not hang if the tracer don't start (OTP-3592)
ip_port_busy(Config) when is_list(Config) ->
- ?line stop(),
- ?line Tracer = dbg:trace_port(ip, 4745),
- ?line Port = Tracer(),
- ?line {error, Reason} = dbg:tracer(port, Tracer),
+ stop(),
+ Tracer = dbg:trace_port(ip, 4745),
+ Port = Tracer(),
+ {error, Reason} = dbg:tracer(port, Tracer),
try
- ?line io:format("Error reason = ~p~n", [Reason]),
- ?line true = port_close(Port)
+ io:format("Error reason = ~p~n", [Reason]),
+ true = port_close(Port)
after
- ?line dbg:stop()
+ dbg:stop()
end,
- ?line ok.
+ ok.
-file_port(suite) ->
- [];
-file_port(doc) ->
- ["Test tracing to file port (simple)"];
+%% Test tracing to file port (simple)
file_port(Config) when is_list(Config) ->
- ?line stop(),
- ?line {A,B,C} = erlang:now(),
- ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
- integer_to_list(B) ++ "-" ++ integer_to_list(C),
- ?line FName = filename:join([?config(data_dir, Config), FTMP]),
- ?line Port = dbg:trace_port(file, FName),
- ?line {ok, _} = dbg:tracer(port, Port),
+ stop(),
+ {A,B,C} = erlang:now(),
+ FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
+ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ FName = filename:join([proplists:get_value(data_dir, Config), FTMP]),
+ Port = dbg:trace_port(file, FName),
+ {ok, _} = dbg:tracer(port, Port),
try
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
- ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
- ?line {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
- ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y),
- ?line ok = dbg:ltp(),
- ?line ok = dbg:ln(),
- ?line stop(),
- ?line dbg:trace_client(file, FName, {fun myhandler/2, self()}),
- ?line S = self(),
- ?line [{trace,S,call,{dbg,ltp,[]},S},
- {trace,S,call,{dbg,ln,[]},hej},
- end_of_trace] = flush()
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
+ {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ {value, {saved, _}} = lists:keysearch(saved, 1, Y),
+ ok = dbg:ltp(),
+ ok = dbg:ln(),
+ stop(),
+ dbg:trace_client(file, FName, {fun myhandler/2, self()}),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]},S},
+ {trace,S,call,{dbg,ln,[]},hej},
+ end_of_trace] = flush()
after
- ?line stop(),
- ?line file:delete(FName)
+ stop(),
+ file:delete(FName)
end,
ok.
-file_port2(suite) ->
- [];
-file_port2(doc) ->
- ["Test tracing to file port with 'follow_file'"];
+%% Test tracing to file port with 'follow_file'
file_port2(Config) when is_list(Config) ->
stop(),
{A,B,C} = erlang:now(),
FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
- "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
- FName = filename:join([?config(data_dir, Config), FTMP]),
+ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ FName = filename:join([proplists:get_value(data_dir, Config), FTMP]),
%% Ok, lets try with flush and follow_file, not a chance on VxWorks
%% with NFS caching...
Port2 = dbg:trace_port(file, FName),
{ok, _} = dbg:tracer(port, Port2),
try
- {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- {ok, _} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
- {ok, _} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
- ok = dbg:ltp(),
- ok = dbg:flush_trace_port(),
- dbg:trace_client(follow_file, FName,
- {fun myhandler/2, self()}),
- S = self(),
- [{trace,S,call,{dbg,ltp,[]},S}] = flush(),
- ok = dbg:ln(),
- ok = dbg:flush_trace_port(),
- receive after 1000 -> ok end, %% Polls every second...
- [{trace,S,call,{dbg,ln,[]},hej}] = flush(),
- stop(),
- [] = flush()
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, _} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ {ok, _} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ ok = dbg:ltp(),
+ ok = dbg:flush_trace_port(),
+ dbg:trace_client(follow_file, FName,
+ {fun myhandler/2, self()}),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]},S}] = flush(),
+ ok = dbg:ln(),
+ ok = dbg:flush_trace_port(),
+ receive after 1000 -> ok end, %% Polls every second...
+ [{trace,S,call,{dbg,ln,[]},hej}] = flush(),
+ stop(),
+ [] = flush()
after
- stop(),
- file:delete(FName)
+ stop(),
+ file:delete(FName)
end,
ok.
-file_port_schedfix(suite) ->
- [];
-file_port_schedfix(doc) ->
- ["Test that the scheduling timestamp fix for trace flag 'running' works."];
+%% Test that the scheduling timestamp fix for trace flag 'running' works.
file_port_schedfix(Config) when is_list(Config) ->
- ?line case (catch erlang:system_info(smp_support)) of
- true ->
- {skip, "No schedule fix on SMP"};
- _ ->
- try
- file_port_schedfix1(Config)
- after
- dbg:stop()
- end
- end.
+ case (catch erlang:system_info(smp_support)) of
+ true ->
+ {skip, "No schedule fix on SMP"};
+ _ ->
+ try
+ file_port_schedfix1(Config)
+ after
+ dbg:stop()
+ end
+ end.
file_port_schedfix1(Config) when is_list(Config) ->
- ?line stop(),
- ?line {A,B,C} = erlang:now(),
- ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
- "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
- ?line FName = filename:join([?config(data_dir, Config), FTMP]),
+ stop(),
+ {A,B,C} = erlang:now(),
+ FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
+ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ FName = filename:join([proplists:get_value(data_dir, Config), FTMP]),
%%
- ?line Port = dbg:trace_port(file, {FName, wrap, ".wraplog", 8*1024, 4}),
- ?line {ok, _} = dbg:tracer(port, Port),
- ?line {ok,[{matched,_node,0}]} = dbg:p(new,[running,procs,send,timestamp]),
+ Port = dbg:trace_port(file, {FName, wrap, ".wraplog", 8*1024, 4}),
+ {ok, _} = dbg:tracer(port, Port),
+ {ok,[{matched,_node,0}]} = dbg:p(new,[running,procs,send,timestamp]),
%%
%% Generate the trace data
%%
@@ -503,146 +665,135 @@ file_port_schedfix1(Config) when is_list(Config) ->
%% execution time. Wallclock. A normal result is about 10 times more
%% without schedule in - schedule out compensation (OTP-3938).
%%
- ?line ok = token_volleyball(3, 4, 8),
+ ok = token_volleyball(3, 4, 8),
+ %%
+ {ok,[{matched,_,_}]} = dbg:p(all, [clear]),
+ stop(),
%%
- ?line {ok,[{matched,_,_}]} = dbg:p(all, [clear]),
- ?line stop(),
- % Some debug code to run on all platforms, for finding the fault on genny
- % Dont touch please /PaN
- ?line io:format("Trace dump by PaN BEGIN~n"),
- ?line dbg:trace_client(file,{FName, wrap, ".wraplog"},{fun(end_of_trace,Pid)-> Pid ! done; (Mesg,Pid) -> io:format("~w~n",[Mesg]),Pid end,self()}),
- receive done -> ok end,
- ?line io:format("Trace dump by PaN END~n"),
- %%
%% Get the trace result
%%
- ?line Tag = make_ref(),
- ?line dbg:trace_client(file, {FName, wrap, ".wraplog"},
- {fun schedstat_handler/2, {self(), Tag, []}}),
- ?line Result =
- receive
- {Tag, D} ->
- lists:map(
- fun({Pid, {A1, B1, C1}}) ->
- {Pid, C1/1000000 + B1 + A1*1000000}
- end,
- D)
- end,
- ?line ok = io:format("Result=~p", [Result]),
-% erlang:display({?MODULE, ?LINE, Result}),
+ Tag = make_ref(),
+ dbg:trace_client(file, {FName, wrap, ".wraplog"},
+ {fun schedstat_handler/2, {self(), Tag, []}}),
+ Result =
+ receive
+ {Tag, D} ->
+ lists:map(
+ fun({Pid, {A1, B1, C1}}) ->
+ {Pid, C1/1000000 + B1 + A1*1000000}
+ end,
+ D)
+ end,
+ ok = io:format("Result=~p", [Result]),
+ % erlang:display({?MODULE, ?LINE, Result}),
%%
%% Analyze the result
%%
- ?line {Min, Max} =
- lists:foldl(
- fun({_Pid, M}, {Mi, Ma}) ->
- {if M < Mi -> M; true -> Mi end,
- if M > Ma -> M; true -> Ma end}
- end,
- {void, 0},
- Result),
+ {Min, Max} = lists:foldl(fun({_Pid, M}, {Mi, Ma}) ->
+ {if M < Mi -> M; true -> Mi end,
+ if M > Ma -> M; true -> Ma end}
+ end,
+ {void, 0},
+ Result),
% More PaN debug
- ?line io:format("Min = ~f, Max = ~f~n",[Min,Max]),
+ io:format("Min = ~f, Max = ~f~n",[Min,Max]),
%%
%% Cleanup
%%
- ?line ToBeDeleted = filelib:wildcard(FName++"*"++".wraplog"),
- ?line lists:map(fun file:delete/1, ToBeDeleted),
-% io:format("ToBeDeleted=~p", [ToBeDeleted]),
+ ToBeDeleted = filelib:wildcard(FName++"*"++".wraplog"),
+ lists:map(fun file:delete/1, ToBeDeleted),
+ % io:format("ToBeDeleted=~p", [ToBeDeleted]),
%%
%% Present the result
%%
P = (Max / Min - 1) * 100,
BottomLine = lists:flatten(io_lib:format("~.2f %", [P])),
if P > 100 ->
- Reason = {BottomLine, '>', "100%"},
- erlang:display({file_port_schedfix, fail, Reason}),
- test_server:fail(Reason);
+ Reason = {BottomLine, '>', "100%"},
+ erlang:display({file_port_schedfix, fail, Reason}),
+ ct:fail(Reason);
true ->
- {comment, BottomLine}
+ {comment, BottomLine}
end.
-wrap_port(suite) ->
- [];
-wrap_port(doc) ->
- ["Test tracing to wrapping file port"];
+%% Test tracing to wrapping file port
wrap_port(Config) when is_list(Config) ->
- ?line Self = self(),
- ?line stop(),
- ?line {A,B,C} = erlang:now(),
- ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
- integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-",
- ?line FName = filename:join([?config(data_dir, Config), FTMP]),
- ?line FNameWildcard = FName++"*"++".trace",
+ Self = self(),
+ stop(),
+ {A,B,C} = erlang:now(),
+ FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
+ integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-",
+ FName = filename:join([proplists:get_value(data_dir, Config), FTMP]),
+ FNameWildcard = FName++"*"++".trace",
%% WrapSize=0 and WrapCnt=11 will force the trace to wrap after
%% every trace message, and to contain only the last 10 entries
%% after trace stop since the last file will be empty waiting
%% for its first trace message.
- ?line WrapSize = 0,
- ?line WrapCnt = 11,
- ?line WrapFilesSpec = {FName, wrap, ".trace", WrapSize, WrapCnt},
- ?line wrap_port_init(WrapFilesSpec),
+ WrapSize = 0,
+ WrapCnt = 11,
+ WrapFilesSpec = {FName, wrap, ".trace", WrapSize, WrapCnt},
+ wrap_port_init(WrapFilesSpec),
%% The number of iterations, N, is tested to place wrap the log,
%% giving a gap in the filename sequence at index 3.
%% This should be a difficult case for
%% the trace_client file sorting functionality.
N = 7,
- ?line lists:foreach(
- fun(Cnt) ->
- ?MODULE:tracee1(Cnt),
- ?MODULE:tracee2(Cnt)
- end,
- lists:seq(1, N)),
- ?line stop(),
+ lists:foreach(
+ fun(Cnt) ->
+ ?MODULE:tracee1(Cnt),
+ ?MODULE:tracee2(Cnt)
+ end,
+ lists:seq(1, N)),
+ stop(),
try
- ?line Files1 = filelib:wildcard(FNameWildcard),
- ?line io:format("~p~n", [Files1]),
- ?line Tc1 = dbg:trace_client(file, WrapFilesSpec,
- {fun myhandler/2, {wait_for_go,Self}}),
- ?line Tref1 = erlang:monitor(process, Tc1),
- Tc1 ! {go,Self},
- ?line [{'DOWN',Tref1,_,_,normal},
- end_of_trace
- |Result] = lists:reverse(flush()),
- ?line M = N - (WrapCnt-1) div 2,
- ?line M = wrap_port_result(Result, Self, N),
- %%
- %% Start a new wrap log with the same name to verify that
- %% all files are cleared at wrap log start. Only produce
- %% two trace messages to also place the gap at index 3,
- %% so the trace log will be misinterpreted.
- %%
- ?line wrap_port_init(WrapFilesSpec),
- ?line Files2 = filelib:wildcard(FNameWildcard),
- ?line io:format("~p~n", [Files2]),
- ?line -1 = ?MODULE:tracee1(-1),
- ?line -1 = ?MODULE:tracee2(-1),
- ?line stop(),
- ?line Files = filelib:wildcard(FNameWildcard),
- ?line io:format("~p~n", [Files]),
- ?line Tc2 = dbg:trace_client(file, WrapFilesSpec,
- {fun myhandler/2, {wait_for_go,Self}}),
- ?line Tref2 = erlang:monitor(process, Tc2),
- Tc2 ! {go,Self},
- ?line [{trace,Self,call,{?MODULE,tracee1,[-1]},Self},
- {trace,Self,call,{?MODULE,tracee2,[-1]},hej},
- end_of_trace,
- {'DOWN',Tref2,_,_,normal}] = flush(),
- %%
- ?line lists:map(fun(F) -> file:delete(F) end, Files)
+ Files1 = filelib:wildcard(FNameWildcard),
+ io:format("~p~n", [Files1]),
+ Tc1 = dbg:trace_client(file, WrapFilesSpec,
+ {fun myhandler/2, {wait_for_go,Self}}),
+ Tref1 = erlang:monitor(process, Tc1),
+ Tc1 ! {go,Self},
+ [{'DOWN',Tref1,_,_,normal},
+ end_of_trace
+ |Result] = lists:reverse(flush()),
+ M = N - (WrapCnt-1) div 2,
+ M = wrap_port_result(Result, Self, N),
+ %%
+ %% Start a new wrap log with the same name to verify that
+ %% all files are cleared at wrap log start. Only produce
+ %% two trace messages to also place the gap at index 3,
+ %% so the trace log will be misinterpreted.
+ %%
+ wrap_port_init(WrapFilesSpec),
+ Files2 = filelib:wildcard(FNameWildcard),
+ io:format("~p~n", [Files2]),
+ -1 = ?MODULE:tracee1(-1),
+ -1 = ?MODULE:tracee2(-1),
+ stop(),
+ Files = filelib:wildcard(FNameWildcard),
+ io:format("~p~n", [Files]),
+ Tc2 = dbg:trace_client(file, WrapFilesSpec,
+ {fun myhandler/2, {wait_for_go,Self}}),
+ Tref2 = erlang:monitor(process, Tc2),
+ Tc2 ! {go,Self},
+ [{trace,Self,call,{?MODULE,tracee1,[-1]},Self},
+ {trace,Self,call,{?MODULE,tracee2,[-1]},hej},
+ end_of_trace,
+ {'DOWN',Tref2,_,_,normal}] = flush(),
+ %%
+ lists:map(fun(F) -> file:delete(F) end, Files)
after
- ?line stop()
+ stop()
end,
ok.
wrap_port_init(WrapFilesSpec) ->
- ?line Port = dbg:trace_port(file, WrapFilesSpec),
- ?line {ok, _} = dbg:tracer(port, Port),
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]),
- ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
- ?line {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]),
- ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y),
+ Port = dbg:trace_port(file, WrapFilesSpec),
+ {ok, _} = dbg:tracer(port, Port),
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]),
+ {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
+ {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]),
+ {value, {saved, _}} = lists:keysearch(saved, 1, Y),
ok.
tracee1(X) ->
@@ -654,106 +805,96 @@ tracee2(X) ->
wrap_port_result([], _S, M) ->
M;
wrap_port_result([{trace, S, call, {?MODULE, tracee2, [M]}, hej},
- {trace, S, call, {?MODULE, tracee1, [M]}, S} | Tail],
- S,
- M) ->
+ {trace, S, call, {?MODULE, tracee1, [M]}, S} | Tail],
+ S,
+ M) ->
wrap_port_result(Tail, S, M-1).
-wrap_port_time(suite) ->
- [];
-wrap_port_time(doc) ->
- ["Test tracing to time limited wrapping file port"];
+%% Test tracing to time limited wrapping file port
wrap_port_time(Config) when is_list(Config) ->
- ?line stop(),
- ?line {A,B,C} = erlang:now(),
- ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
- integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-",
- ?line FName = filename:join([?config(data_dir, Config), FTMP]),
+ stop(),
+ {A,B,C} = erlang:now(),
+ FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
+ integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-",
+ FName = filename:join([proplists:get_value(data_dir, Config), FTMP]),
%% WrapTime=2 and WrapCnt=4 will force the trace to wrap after
%% every 2 seconds, and to contain between 3*2 and 4*2 seconds
%% of trace entries.
- ?line WrapFilesSpec = {FName, wrap, ".trace", {time, 2000}, 4},
- ?line Port = dbg:trace_port(file, WrapFilesSpec),
- ?line {ok, _} = dbg:tracer(port, Port),
+ WrapFilesSpec = {FName, wrap, ".trace", {time, 2000}, 4},
+ Port = dbg:trace_port(file, WrapFilesSpec),
+ {ok, _} = dbg:tracer(port, Port),
try
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]),
- ?line {value, {saved, _Saved1}} = lists:keysearch(saved, 1, X),
- ?line {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]),
- ?line {value, {saved, _Saved2}} = lists:keysearch(saved, 1, Y),
- %% The delays in the iterations places two trace messages in each
- %% trace file, but the last which is empty waiting for its first
- %% trace message. The number of iterations is chosen so that
- %% one trace file has been wasted, and therefore the first pair
- %% of trace messages.
- ?line lists:foreach(
- fun(Cnt) ->
- receive after 1000 -> ok end,
- ?MODULE:tracee1(Cnt),
- ?MODULE:tracee2(Cnt),
- receive after 1100 -> ok end
- end,
- lists:seq(1, 4)),
- ?line stop(),
- ?line Files = filelib:wildcard(FName ++ "*" ++ ".trace"),
- ?line io:format("~p~n", [Files]),
- ?line dbg:trace_client(file, WrapFilesSpec, {fun myhandler/2, self()}),
- ?line S = self(),
- ?line [{trace, S, call, {?MODULE, tracee1, [2]}, S},
- {trace, S, call, {?MODULE, tracee2, [2]}, hej},
- {trace, S, call, {?MODULE, tracee1, [3]}, S},
- {trace, S, call, {?MODULE, tracee2, [3]}, hej},
- {trace, S, call, {?MODULE, tracee1, [4]}, S},
- {trace, S, call, {?MODULE, tracee2, [4]}, hej},
- end_of_trace] = flush(),
- ?line lists:map(fun(F) -> file:delete(F) end, Files)
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]),
+ {value, {saved, _Saved1}} = lists:keysearch(saved, 1, X),
+ {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]),
+ {value, {saved, _Saved2}} = lists:keysearch(saved, 1, Y),
+ %% The delays in the iterations places two trace messages in each
+ %% trace file, but the last which is empty waiting for its first
+ %% trace message. The number of iterations is chosen so that
+ %% one trace file has been wasted, and therefore the first pair
+ %% of trace messages.
+ lists:foreach(
+ fun(Cnt) ->
+ receive after 1000 -> ok end,
+ ?MODULE:tracee1(Cnt),
+ ?MODULE:tracee2(Cnt),
+ receive after 1100 -> ok end
+ end,
+ lists:seq(1, 4)),
+ stop(),
+ Files = filelib:wildcard(FName ++ "*" ++ ".trace"),
+ io:format("~p~n", [Files]),
+ dbg:trace_client(file, WrapFilesSpec, {fun myhandler/2, self()}),
+ S = self(),
+ [{trace, S, call, {?MODULE, tracee1, [2]}, S},
+ {trace, S, call, {?MODULE, tracee2, [2]}, hej},
+ {trace, S, call, {?MODULE, tracee1, [3]}, S},
+ {trace, S, call, {?MODULE, tracee2, [3]}, hej},
+ {trace, S, call, {?MODULE, tracee1, [4]}, S},
+ {trace, S, call, {?MODULE, tracee2, [4]}, hej},
+ end_of_trace] = flush(),
+ lists:map(fun(F) -> file:delete(F) end, Files)
after
- ?line stop()
+ stop()
end,
ok.
-with_seq_trace(suite) ->
- [];
-with_seq_trace(doc) ->
- ["Test ordinary tracing combined with seq_trace"];
+%% Test ordinary tracing combined with seq_trace
with_seq_trace(Config) when is_list(Config) ->
try
- ?line {ok, Server} = start(),
- ?line {ok, Tracer} = dbg:get_tracer(),
- ?line {ok, X} = dbg:tp(dbg, get_tracer, [{[],[],
- [{set_seq_token, send, true}]}]),
- ?line {value, {saved, _}} = lists:keysearch(saved, 1, X),
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line seq_trace:set_system_tracer(Tracer),
- ?line dbg:get_tracer(),
- receive
- after 1 ->
- ok
- end,
- ?line S = self(),
- ?line ThisNode = node(),
- ?line [{trace,S,call,{dbg,get_tracer,[]}},
- {seq_trace,0,{send,_,S,Server,{S,{get_tracer,ThisNode}}}},
- {seq_trace,0,{send,_,Server,S,{dbg,{ok,Tracer}}}}] =
- flush()
+ {ok, Server} = start(),
+ {ok, Tracer} = dbg:get_tracer(),
+ {ok, X} = dbg:tp(dbg, get_tracer, [{[],[],
+ [{set_seq_token, send, true}]}]),
+ {value, {saved, _}} = lists:keysearch(saved, 1, X),
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ seq_trace:set_system_tracer(Tracer),
+ dbg:get_tracer(),
+ receive
+ after 1 ->
+ ok
+ end,
+ S = self(),
+ ThisNode = node(),
+ [{trace,S,call,{dbg,get_tracer,[]}},
+ {seq_trace,0,{send,_,S,Server,{S,{get_tracer,ThisNode}}}},
+ {seq_trace,0,{send,_,Server,S,{dbg,{ok,Tracer}}}}] =
+ flush()
after
- ?line stop()
+ stop()
end,
ok.
-dead_suspend(suite) ->
- [];
-dead_suspend(doc) ->
- ["Test that trace messages concerning a now dead process does "
- "not crash dbg."];
+%% Test that trace messages concerning a now dead process does not crash dbg.
dead_suspend(Config) when is_list(Config) ->
- ?line start(),
+ start(),
try
- survived = run_dead_suspend()
+ survived = run_dead_suspend()
after
- ?line stop()
+ stop()
end.
run_dead_suspend() ->
@@ -766,10 +907,10 @@ run_dead_suspend() ->
spawn(?MODULE, dummy, []),
receive after 1000 -> ok end,
case whereis(dbg) of
- undefined ->
- died;
- _ ->
- survived
+ undefined ->
+ died;
+ _ ->
+ survived
end.
dummy() ->
@@ -781,10 +922,10 @@ tracer_exit_on_stop(_) ->
%% Tracer blocks waiting for fun to complete so that the trace message and
%% the exit signal message from the dbg process are in its message queue.
Fun = fun() ->
- ?MODULE:dummy(),
- Ref = erlang:trace_delivered(self()),
- receive {trace_delivered, _, Ref} -> stop() end
- end,
+ ?MODULE:dummy(),
+ Ref = erlang:trace_delivered(self()),
+ receive {trace_delivered, _, Ref} -> stop() end
+ end,
{ok, _} = dbg:tracer(process, {fun spawn_once_handler/2, {self(), Fun}}),
{ok, Tracer} = dbg:get_tracer(),
MRef = monitor(process, Tracer),
@@ -803,9 +944,9 @@ spawn_once_handler(Event, {Pid, done} = State) ->
spawn_once_handler(Event, {Pid, Fun}) ->
{_, Ref} = spawn_monitor(Fun),
receive
- {'DOWN', Ref, _, _, _} ->
- Pid ! Event,
- {Pid, done}
+ {'DOWN', Ref, _, _, _} ->
+ Pid ! Event,
+ {Pid, done}
end.
%% Test that erl_tracer modules work correctly
@@ -898,38 +1039,38 @@ wait_node(_,0) ->
no;
wait_node(Node, N) ->
case net_adm:ping(Node) of
- pong ->
- ok;
- pang ->
- receive
- after 1000 ->
- ok
- end,
- wait_node(Node, N - 1)
+ pong ->
+ ok;
+ pang ->
+ receive
+ after 1000 ->
+ ok
+ end,
+ wait_node(Node, N - 1)
end.
myhandler(Message, {wait_for_go,Pid}) ->
receive
- {go,Pid} ->
- myhandler(Message, Pid)
+ {go,Pid} ->
+ myhandler(Message, Pid)
end;
myhandler(Message, Relay) ->
Relay ! Message,
case Message of
- end_of_trace ->
- ok;
- _ ->
- Relay
+ end_of_trace ->
+ ok;
+ _ ->
+ Relay
end.
flush() ->
flush([]).
flush(Acc) ->
receive
- X ->
- flush(Acc ++ [X])
+ X ->
+ flush(Acc ++ [X])
after 1000 ->
- Acc
+ Acc
end.
start() ->
@@ -943,88 +1084,92 @@ stop() ->
schedstat_handler(TraceMsg, {Parent, Tag, Data} = State) ->
case TraceMsg of
- {trace_ts, Pid, in, _, Ts} ->
- NewData =
- case lists:keysearch(Pid, 1, Data) of
- {value, {Pid, Acc}} ->
- [{Pid, Acc, Ts} | lists:keydelete(Pid, 1, Data)];
- false ->
- [{Pid, {0, 0, 0}, Ts} | Data];
- Other ->
- exit(Parent, {?MODULE, ?LINE, Other}),
- erlang:display({?MODULE, ?LINE, Other}),
- Data
- end,
- {Parent, Tag, NewData};
- {trace_ts, Pid, out, _, {A3, B3, C3}} ->
- NewData =
- case lists:keysearch(Pid, 1, Data) of
- {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} ->
- [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} |
- lists:keydelete(Pid, 1, Data)];
- Other ->
- exit(Parent, {?MODULE, ?LINE, Other}),
- erlang:display({?MODULE, ?LINE, Other}),
- Data
- end,
- {Parent, Tag, NewData};
- {trace_ts, Pid, exit, normal, {A3, B3, C3}} ->
- NewData =
- case lists:keysearch(Pid, 1, Data) of
- {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} ->
- [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} |
- lists:keydelete(Pid, 1, Data)];
- {value, {Pid, _Acc}} ->
- Data;
- false ->
- [{Pid, {0, 0, 0}} | Data];
- Other ->
- exit(Parent, {?MODULE, ?LINE, Other}),
- erlang:display({?MODULE, ?LINE, Other}),
- Data
- end,
- {Parent, Tag, NewData};
- {trace_ts, _Pid, send, _Msg, _OtherPid, _Ts} ->
- State;
- end_of_trace ->
- Parent ! {Tag, Data},
- State
+ {trace_ts, Pid, in, _, Ts} ->
+ NewData =
+ case lists:keysearch(Pid, 1, Data) of
+ {value, {Pid, Acc}} ->
+ [{Pid, Acc, Ts} | lists:keydelete(Pid, 1, Data)];
+ false ->
+ [{Pid, {0, 0, 0}, Ts} | Data];
+ Other ->
+ exit(Parent, {?MODULE, ?LINE, Other}),
+ erlang:display({?MODULE, ?LINE, Other}),
+ Data
+ end,
+ {Parent, Tag, NewData};
+ {trace_ts, Pid, out, _, {A3, B3, C3}} ->
+ NewData =
+ case lists:keysearch(Pid, 1, Data) of
+ {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} ->
+ [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} |
+ lists:keydelete(Pid, 1, Data)];
+ Other ->
+ exit(Parent, {?MODULE, ?LINE, Other}),
+ erlang:display({?MODULE, ?LINE, Other}),
+ Data
+ end,
+ {Parent, Tag, NewData};
+ {trace_ts,_Pid,spawned,_OtherPid,_,_Ts} ->
+ State;
+ {trace_ts,_Pid,getting_linked,_OtherPid,_Ts} ->
+ State;
+ {trace_ts, Pid, exit, normal, {A3, B3, C3}} ->
+ NewData =
+ case lists:keysearch(Pid, 1, Data) of
+ {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} ->
+ [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} |
+ lists:keydelete(Pid, 1, Data)];
+ {value, {Pid, _Acc}} ->
+ Data;
+ false ->
+ [{Pid, {0, 0, 0}} | Data];
+ Other ->
+ exit(Parent, {?MODULE, ?LINE, Other}),
+ erlang:display({?MODULE, ?LINE, Other}),
+ Data
+ end,
+ {Parent, Tag, NewData};
+ {trace_ts, _Pid, send, _Msg, _OtherPid, _Ts} ->
+ State;
+ end_of_trace ->
+ Parent ! {Tag, Data},
+ State
end.
pass_token(Token, Next, Loops) ->
receive
- {Token, 1} = Msg ->
- sendloop(Loops),
- Next ! Msg;
- {Token, _Cnt} = Msg->
- sendloop(Loops),
- Next ! Msg,
- pass_token(Token, Next, Loops)
+ {Token, 1} = Msg ->
+ sendloop(Loops),
+ Next ! Msg;
+ {Token, _Cnt} = Msg->
+ sendloop(Loops),
+ Next ! Msg,
+ pass_token(Token, Next, Loops)
end.
pass_token(Token, Final, Cnt, Loops) ->
receive
- {Token, start, Next} ->
- sendloop(Loops),
- Msg = {Token, Cnt},
- Next ! Msg,
- pass_token(Token, Final, Next, Cnt, Loops)
+ {Token, start, Next} ->
+ sendloop(Loops),
+ Msg = {Token, Cnt},
+ Next ! Msg,
+ pass_token(Token, Final, Next, Cnt, Loops)
end.
pass_token(Token, Final, Next, Cnt, Loops) ->
receive
- {Token, 1} ->
- sendloop(Loops),
- Msg = {Token, done},
- Final ! Msg;
- {Token, Cnt} ->
- sendloop(Loops),
- NextCnt = Cnt-1,
- Msg = {Token, NextCnt},
- Next ! Msg,
- pass_token(Token, Final, Next, NextCnt, Loops)
+ {Token, 1} ->
+ sendloop(Loops),
+ Msg = {Token, done},
+ Final ! Msg;
+ {Token, Cnt} ->
+ sendloop(Loops),
+ NextCnt = Cnt-1,
+ Msg = {Token, NextCnt},
+ Next ! Msg,
+ pass_token(Token, Final, Next, NextCnt, Loops)
end.
sendloop(Loops) ->
diff --git a/lib/runtime_tools/test/dyntrace_SUITE.erl b/lib/runtime_tools/test/dyntrace_SUITE.erl
index f3c1cce8f8..7be2f49a8b 100644
--- a/lib/runtime_tools/test/dyntrace_SUITE.erl
+++ b/lib/runtime_tools/test/dyntrace_SUITE.erl
@@ -20,26 +20,14 @@
-module(dyntrace_SUITE).
-include_lib("common_test/include/ct.hrl").
--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,init_per_suite/1, end_per_suite/1]).
%% Test cases
-export([smoke/1,process/1]).
-%% Default timetrap timeout (set in init_per_testcase)
--define(default_timeout, ?t:minutes(1)).
-
-init_per_testcase(_Case, Config) ->
- Dog = test_server: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]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
case erlang:system_info(dynamic_trace) of
@@ -73,14 +61,8 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
smoke(Config) ->
- Emu = ?t:lookup_config(emu_name, Config),
+ Emu = test_server:lookup_config(emu_name, Config),
BinEmu = list_to_binary(Emu),
case erlang:system_info(dynamic_trace) of
dtrace ->
@@ -107,8 +89,7 @@ process(_Config) ->
{probe,"process-hibernate"},
{action,[{printf,["hibernate %s %s\n",{arg,0},{arg,1}]}]},
{probe,"process-exit"},
- {action,[{printf,["exit %s %s\n",{arg,0},{arg,1}]}]}
- ],
+ {action,[{printf,["exit %s %s\n",{arg,0},{arg,1}]}]}],
F = fun() ->
{Pid,Ref} = spawn_monitor(fun my_process/0),
Pid ! hibernate,
diff --git a/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl b/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl
new file mode 100644
index 0000000000..e6c147b003
--- /dev/null
+++ b/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl
@@ -0,0 +1,377 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(dyntrace_lttng_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-export([all/0, suite/0]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([t_lttng_list/1,
+ t_procs/1,
+ t_ports/1,
+ t_running_process/1,
+ t_running_port/1,
+ t_call/1,
+ t_call_return_to/1,
+ t_call_silent/1,
+ t_send/1,
+ t_receive/1,
+ t_garbage_collection/1,
+ t_all/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
+
+all() ->
+ [t_lttng_list,
+ t_procs,
+ t_ports,
+ t_running_process,
+ t_running_port,
+ t_call,
+ t_call_return_to,
+ t_call_silent,
+ t_send,
+ t_receive,
+ t_garbage_collection,
+ t_all].
+
+
+init_per_suite(Config) ->
+ case erlang:system_info(dynamic_trace) of
+ lttng ->
+ ensure_lttng_stopped("--all"),
+ Config;
+ _ ->
+ {skip, "No LTTng configured on system."}
+ end.
+
+end_per_suite(_Config) ->
+ ensure_lttng_stopped("--all"),
+ ok.
+
+init_per_testcase(Case, Config) ->
+ %% ensure loaded
+ _ = dyntrace:module_info(),
+ Name = atom_to_list(Case),
+ ok = ensure_lttng_started(Name, Config),
+ [{session, Name}|Config].
+
+end_per_testcase(Case, _Config) ->
+ Name = atom_to_list(Case),
+ ok = ensure_lttng_stopped(Name),
+ ok.
+
+%% tracepoints
+%%
+%% com_ericsson_dyntrace:gc_major_end
+%% com_ericsson_dyntrace:gc_major_start
+%% com_ericsson_dyntrace:gc_minor_end
+%% com_ericsson_dyntrace:gc_minor_start
+%% com_ericsson_dyntrace:message_receive
+%% com_ericsson_dyntrace:message_send
+%% -com_ericsson_dyntrace:message_queued
+%% com_ericsson_dyntrace:function_exception
+%% com_ericsson_dyntrace:function_return
+%% com_ericsson_dyntrace:function_call
+%% com_ericsson_dyntrace:port_link
+%% com_ericsson_dyntrace:port_exit
+%% com_ericsson_dyntrace:port_open
+%% com_ericsson_dyntrace:port_scheduled
+%% com_ericsson_dyntrace:process_scheduled
+%% com_ericsson_dyntrace:process_register
+%% com_ericsson_dyntrace:process_exit
+%% com_ericsson_dyntrace:process_link
+%% com_ericsson_dyntrace:process_spawn
+%%
+%% Testcases
+%%
+
+t_lttng_list(_Config) ->
+ {ok, _} = cmd("lttng list -u"),
+ ok.
+
+t_procs(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:process_*", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},procs]),
+
+ Pid = spawn_link(fun() -> waiter() end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+ timer:sleep(1000),
+
+ _ = erlang:trace(all, false, [procs]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_spawn", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_link", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_exit", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_register", Res),
+ ok.
+
+t_ports(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:port_*", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},ports]),
+
+ _ = os:cmd("ls"),
+
+ _ = erlang:trace(all, false, [{tracer, dyntrace, []},ports]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_open", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_link", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_exit", Res),
+ ok.
+
+t_running_process(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:process_scheduled", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},running]),
+
+ Pid = spawn_link(fun() -> waiter() end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+ timer:sleep(1000),
+
+ _ = erlang:trace(all, false, [running]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_scheduled", Res),
+ ok.
+
+t_running_port(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:port_scheduled", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},running_ports]),
+
+ _ = os:cmd("ls"),
+ _ = os:cmd("ls"),
+
+ _ = erlang:trace(all, false, [running_ports]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_scheduled", Res),
+ ok.
+
+
+t_call(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call]),
+ _ = erlang:trace_pattern({?MODULE, '_', '_'}, [{'_',[],[{exception_trace}]}], [local]),
+
+ DontLink = spawn(fun() -> foo_clause_exception(nope) end),
+ Pid = spawn_link(fun() -> waiter() end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+
+ timer:sleep(10),
+ undefined = erlang:process_info(DontLink),
+
+ _ = erlang:trace_pattern({?MODULE, '_', '_'}, false, [local]),
+ _ = erlang:trace(all, false, [call]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:function_call", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:function_return", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:function_exception", Res),
+ ok.
+
+t_send(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:message_send", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},send]),
+
+ Pid = spawn_link(fun() -> waiter() end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+ _ = os:cmd("ls"),
+ timer:sleep(10),
+
+ _ = erlang:trace(all, false, [send]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:message_send", Res),
+ ok.
+
+t_call_return_to(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call, return_to]),
+ _ = erlang:trace_pattern({lists, '_', '_'}, true, [local]),
+ _ = erlang:trace_pattern({?MODULE, '_', '_'}, true, [local]),
+
+ Pid = spawn_link(fun() -> gcfier(10) end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+ timer:sleep(10),
+
+ _ = erlang:trace_pattern({?MODULE, '_', '_'}, false, [local]),
+ _ = erlang:trace_pattern({lists, '_', '_'}, false, [local]),
+ _ = erlang:trace(all, false, [call,return_to]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:function_call", Res),
+ ok.
+
+t_call_silent(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call, silent]),
+ _ = erlang:trace_pattern({?MODULE, '_', '_'}, [{'_',[],[{exception_trace}]}], [local]),
+
+ DontLink = spawn(fun() -> foo_clause_exception(nope) end),
+ Pid = spawn_link(fun() -> waiter() end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+
+ timer:sleep(10),
+ undefined = erlang:process_info(DontLink),
+
+ _ = erlang:trace_pattern({?MODULE, '_', '_'}, false, [local]),
+ _ = erlang:trace(all, false, [call]),
+ Res = lttng_stop_and_view(Config),
+ notfound = check_tracepoint("com_ericsson_dyntrace:function_call", Res),
+ notfound = check_tracepoint("com_ericsson_dyntrace:function_return", Res),
+ notfound = check_tracepoint("com_ericsson_dyntrace:function_exception", Res),
+ ok.
+
+
+t_receive(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:message_receive", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},'receive']),
+
+ Pid = spawn_link(fun() -> waiter() end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+ timer:sleep(10),
+ _ = erlang:trace(all, false, ['receive']),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:message_receive", Res),
+ ok.
+
+t_garbage_collection(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:gc_*", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},garbage_collection]),
+
+ Pid = spawn_link(fun() -> gcfier() end),
+ Pid ! {self(), ok},
+ ok = receive {Pid,ok} -> ok end,
+ timer:sleep(10),
+ _ = erlang:trace(all, false, [garbage_collection]),
+ Res = lttng_stop_and_view(Config),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_major_start", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_major_end", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_start", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_end", Res),
+ ok.
+
+t_all(Config) when is_list(Config) ->
+ ok = lttng_start_event("com_ericsson_dyntrace:*", Config),
+ _ = erlang:trace(new, true, [{tracer, dyntrace, []},all]),
+
+ Pid1 = spawn_link(fun() -> waiter() end),
+ Pid1 ! {self(), ok},
+ ok = receive {Pid1,ok} -> ok end,
+
+ Pid2 = spawn_link(fun() -> gcfier() end),
+ Pid2 ! {self(), ok},
+ ok = receive {Pid2,ok} -> ok end,
+ _ = os:cmd("ls"),
+ _ = os:cmd("ls"),
+ timer:sleep(10),
+
+ _ = erlang:trace(all, false, [all]),
+ Res = lttng_stop_and_view(Config),
+
+ ok = check_tracepoint("com_ericsson_dyntrace:process_spawn", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_link", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_exit", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_register", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_open", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_link", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_exit", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:process_scheduled", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:port_scheduled", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:message_send", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:message_receive", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_major_start", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_major_end", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_start", Res),
+ ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_end", Res),
+ ok.
+
+
+%% aux
+
+gcfier() ->
+ gcfier(10000).
+gcfier(N) ->
+ receive
+ {Pid, ok} ->
+ _ = lists:reverse(lists:seq(1,N)),
+ true = erlang:garbage_collect(),
+ Pid ! {self(), ok}
+ end.
+
+
+waiter() ->
+ true = register(?MODULE, self()),
+ receive
+ {Pid, ok} ->
+ Child = spawn(fun() -> receive ok -> ok end end),
+ link(Child),
+ unlink(Child),
+ _ = lists:seq(1,1000),
+ Child ! ok,
+ true = unregister(?MODULE),
+ Pid ! {self(),ok}
+ end.
+
+foo_clause_exception({1,2}) -> badness.
+
+%% lttng
+lttng_stop_and_view(Config) ->
+ Path = proplists:get_value(priv_dir, Config),
+ Name = proplists:get_value(session, Config),
+ {ok,_} = cmd("lttng stop " ++ Name),
+ {ok,Res} = cmd("lttng view " ++ Name ++ " --trace-path=" ++ Path),
+ Res.
+
+check_tracepoint(TP, Data) ->
+ case re:run(Data, TP, [global]) of
+ {match, _} -> ok;
+ _ -> notfound
+ end.
+
+lttng_start_event(Event, Config) ->
+ Name = proplists:get_value(session, Config),
+ {ok, _} = cmd("lttng enable-event -u " ++ Event ++ " --session=" ++ Name),
+ {ok, _} = cmd("lttng start " ++ Name),
+ ok.
+
+ensure_lttng_started(Name, Config) ->
+ Out = case proplists:get_value(priv_dir, Config) of
+ undefined -> [];
+ Path -> "--output="++Path++" "
+ end,
+ {ok,_} = cmd("lttng create " ++ Out ++ Name),
+ ok.
+
+ensure_lttng_stopped(Name) ->
+ {ok,_} = cmd("lttng stop"),
+ {ok,_} = cmd("lttng destroy " ++ Name),
+ ok.
+
+cmd(Cmd) ->
+ io:format("<< ~ts~n", [Cmd]),
+ Res = os:cmd(Cmd),
+ io:format(">> ~ts~n", [Res]),
+ {ok,Res}.
diff --git a/lib/runtime_tools/test/erts_alloc_config_SUITE.erl b/lib/runtime_tools/test/erts_alloc_config_SUITE.erl
index 4fb96a0c1b..6ae51d9a26 100644
--- a/lib/runtime_tools/test/erts_alloc_config_SUITE.erl
+++ b/lib/runtime_tools/test/erts_alloc_config_SUITE.erl
@@ -25,9 +25,7 @@
-include_lib("common_test/include/ct.hrl").
%-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([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).
%% Testcases
-export([basic/1]).
@@ -35,83 +33,63 @@
%% internal export
-export([make_basic_config/1]).
--define(DEFAULT_TIMEOUT, ?t:minutes(2)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
all() ->
[basic].
-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) when is_list(Config) ->
[{testcase, Case},
- {watchdog, ?t:timetrap(?DEFAULT_TIMEOUT)},
{erl_flags_env, save_env()} | Config].
end_per_testcase(_Case, Config) when is_list(Config) ->
- ?t:timetrap_cancel(?config(watchdog, Config)),
- restore_env(?config(erl_flags_env, Config)),
+ restore_env(proplists:get_value(erl_flags_env, Config)),
ok.
%%%
%%% The test cases ------------------------------------------------------------
%%%
-basic(doc) -> [];
-basic(suite) -> [];
basic(Config) when is_list(Config) ->
- ?line ErtsAllocConfig = privfile("generated", Config),
+ ErtsAllocConfig = privfile("generated", Config),
SbctMod = " +MBsbct 1024 +MHsbct 4096",
%% Make sure we have enabled allocators
ZFlgs = os:getenv("ERL_ZFLAGS", "") ++ " +Mea max +Mea config",
- ?line os:putenv("ERL_ZFLAGS", ZFlgs ++ SbctMod),
+ os:putenv("ERL_ZFLAGS", ZFlgs ++ SbctMod),
- ?line {ok, Node1} = start_node(Config),
- ?line ok = rpc:call(Node1, ?MODULE, make_basic_config, [ErtsAllocConfig]),
- ?line stop_node(Node1),
+ {ok, Node1} = start_node(Config),
+ ok = rpc:call(Node1, ?MODULE, make_basic_config, [ErtsAllocConfig]),
+ stop_node(Node1),
- ?line display_file(ErtsAllocConfig),
+ display_file(ErtsAllocConfig),
- ?line ManualConfig = privfile("manual", Config),
- ?line {ok, IOD} = file:open(ManualConfig, [write]),
- ?line io:format(IOD, "~s", ["+MBsbct 2048"]),
- ?line file:close(IOD),
- ?line display_file(ManualConfig),
+ ManualConfig = privfile("manual", Config),
+ {ok, IOD} = file:open(ManualConfig, [write]),
+ io:format(IOD, "~s", ["+MBsbct 2048"]),
+ file:close(IOD),
+ display_file(ManualConfig),
- ?line os:putenv("ERL_ZFLAGS", ZFlgs),
+ os:putenv("ERL_ZFLAGS", ZFlgs),
- ?line {ok, Node2} = start_node(Config,
- "-args_file " ++ ErtsAllocConfig
- ++ " -args_file " ++ ManualConfig),
+ {ok, Node2} = start_node(Config,
+ "-args_file " ++ ErtsAllocConfig
+ ++ " -args_file " ++ ManualConfig),
- ?line {_, _, _, Cfg} = rpc:call(Node2, erlang, system_info, [allocator]),
+ {_, _, _, Cfg} = rpc:call(Node2, erlang, system_info, [allocator]),
- ?line stop_node(Node2),
+ stop_node(Node2),
- ?line {value,{binary_alloc, BCfg}} = lists:keysearch(binary_alloc, 1, Cfg),
- ?line {value,{sbct, 2097152}} = lists:keysearch(sbct, 1, BCfg),
- ?line {value,{eheap_alloc, HCfg}} = lists:keysearch(eheap_alloc, 1, Cfg),
- ?line {value,{sbct, 4194304}} = lists:keysearch(sbct, 1, HCfg),
+ {value,{binary_alloc, BCfg}} = lists:keysearch(binary_alloc, 1, Cfg),
+ {value,{sbct, 2097152}} = lists:keysearch(sbct, 1, BCfg),
+ {value,{eheap_alloc, HCfg}} = lists:keysearch(eheap_alloc, 1, Cfg),
+ {value,{sbct, 4194304}} = lists:keysearch(sbct, 1, HCfg),
- ?line ok.
+ ok.
make_basic_config(ErtsAllocConfig) ->
%% Save some different scenarios
@@ -119,35 +97,35 @@ make_basic_config(ErtsAllocConfig) ->
SSBegun = make_ref(),
SSDone = make_ref(),
SSFun = fun (F) ->
- receive
- SSDone ->
- ok = erts_alloc_config:save_scenario(),
- Tester ! SSDone
- after 500 ->
- ok = erts_alloc_config:save_scenario(),
- F(F)
- end
- end,
+ receive
+ SSDone ->
+ ok = erts_alloc_config:save_scenario(),
+ Tester ! SSDone
+ after 500 ->
+ ok = erts_alloc_config:save_scenario(),
+ F(F)
+ end
+ end,
SS = spawn_link(fun () ->
- ok = erts_alloc_config:save_scenario(),
- Tester ! SSBegun,
- SSFun(SSFun)
- end),
+ ok = erts_alloc_config:save_scenario(),
+ Tester ! SSBegun,
+ SSFun(SSFun)
+ end),
receive SSBegun -> ok end,
Ref = make_ref(),
Tab = ets:new(?MODULE, [bag, public]),
Ps = lists:map(
- fun (_) ->
- spawn_link(
- fun () ->
- ets:insert(Tab,
- {self(),
- lists:seq(1, 1000)}),
- receive after 1000 -> ok end,
- Tester ! {Ref, self()}
- end)
- end,
- lists:seq(1, 10000)),
+ fun (_) ->
+ spawn_link(
+ fun () ->
+ ets:insert(Tab,
+ {self(),
+ lists:seq(1, 1000)}),
+ receive after 1000 -> ok end,
+ Tester ! {Ref, self()}
+ end)
+ end,
+ lists:seq(1, 10000)),
lists:foreach(fun (P) -> receive {Ref, P} -> ok end end, Ps),
ets:delete(Tab),
SS ! SSDone,
@@ -162,35 +140,35 @@ make_basic_config(ErtsAllocConfig) ->
%%
display_file(FileName) ->
- ?t:format("filename: ~s~n", [FileName]),
+ io:format("filename: ~s~n", [FileName]),
{ok, Bin} = file:read_file(FileName),
io:format("~s", [binary_to_list(Bin)]),
- ?t:format("eof: ~s~n", [FileName]),
+ io:format("eof: ~s~n", [FileName]),
ok.
mk_name(Config) when is_list(Config) ->
{A, B, C} = now(),
list_to_atom(atom_to_list(?MODULE)
- ++ "-" ++ atom_to_list(?config(testcase, Config))
- ++ "-" ++ integer_to_list(A)
- ++ "-" ++ integer_to_list(B)
- ++ "-" ++ integer_to_list(C)).
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(A)
+ ++ "-" ++ integer_to_list(B)
+ ++ "-" ++ integer_to_list(C)).
start_node(Config) ->
start_node(Config, "").
start_node(Config, Args) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line ?t:start_node(mk_name(Config),
- slave,
- [{args, "-pa " ++ Pa ++ " " ++ Args}]).
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(mk_name(Config),
+ slave,
+ [{args, "-pa " ++ Pa ++ " " ++ Args}]).
stop_node(Node) ->
- ?line true = ?t:stop_node(Node).
+ true = test_server:stop_node(Node).
privfile(Name, Config) ->
- filename:join([?config(priv_dir, Config),
- atom_to_list(?config(testcase, Config)) ++ "." ++ Name]).
+ filename:join([proplists:get_value(priv_dir, Config),
+ atom_to_list(proplists:get_value(testcase, Config)) ++ "." ++ Name]).
save_env() ->
{erl_flags,
@@ -203,15 +181,15 @@ restore_env(EVar, false) when is_list(EVar) ->
restore_env(EVar, "");
restore_env(EVar, "") when is_list(EVar) ->
case os:getenv(EVar) of
- false -> ok;
- "" -> ok;
- " " -> ok;
- _ -> os:putenv(EVar, " ")
+ false -> ok;
+ "" -> ok;
+ " " -> ok;
+ _ -> os:putenv(EVar, " ")
end;
restore_env(EVar, Value) when is_list(EVar), is_list(Value) ->
case os:getenv(EVar) of
- Value -> ok;
- _ -> os:putenv(EVar, Value)
+ Value -> ok;
+ _ -> os:putenv(EVar, Value)
end.
restore_env({erl_flags, AFlgs, Flgs, RFlgs, ZFlgs}) ->
diff --git a/lib/runtime_tools/test/runtime_tools_SUITE.erl b/lib/runtime_tools/test/runtime_tools_SUITE.erl
index 374d7f6694..6877e1a379 100644
--- a/lib/runtime_tools/test/runtime_tools_SUITE.erl
+++ b/lib/runtime_tools/test/runtime_tools_SUITE.erl
@@ -21,54 +21,27 @@
-include_lib("common_test/include/ct.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]).
%% Test cases
-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)).
-
-init_per_testcase(_Case, Config) ->
- Dog = test_server: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]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
all() ->
[app_file,
appup_file,
start_stop_app].
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
app_file(_Config) ->
- ?line ok = ?t:app_test(runtime_tools),
+ ok = test_server:app_test(runtime_tools),
ok.
appup_file(_Config) ->
- ok = ?t:appup_test(runtime_tools).
+ ok = test_server:appup_test(runtime_tools).
start_stop_app(_Config) ->
ok = application:start(runtime_tools),
diff --git a/lib/runtime_tools/test/system_information_SUITE.erl b/lib/runtime_tools/test/system_information_SUITE.erl
index 5e2e0d17ac..a5a025a1cf 100644
--- a/lib/runtime_tools/test/system_information_SUITE.erl
+++ b/lib/runtime_tools/test/system_information_SUITE.erl
@@ -230,19 +230,19 @@ api_report(_Config) ->
ok.
api_to_file(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Filename = filename:join([DataDir, "system_information_report_1.dat"]),
ok = system_information:to_file(Filename),
{ok, _} = file:consult(Filename),
{save_config, [{report_name, Filename}]}.
api_from_file(Config) ->
- {api_to_file, Saved} = ?config(saved_config, Config),
- DataDir = ?config(data_dir, Config),
+ {api_to_file, Saved} = proplists:get_value(saved_config, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Fname1 = filename:join([DataDir, "information_test_report.dat"]),
Report1 = system_information:from_file(Fname1),
ok = validate_report(Report1),
- Fname2 = ?config(report_name, Saved),
+ Fname2 = proplists:get_value(report_name, Saved),
Report2 = system_information:from_file(Fname2),
ok = validate_report(Report2),
ok.
@@ -253,7 +253,7 @@ api_start_stop(_Config) ->
ok.
validate_server_interface(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Fname1 = filename:join([DataDir, "information_test_report.dat"]),
%% load old report
ok = system_information:load_report(file, Fname1),
diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml
index a57068492c..6fbdcb9f5b 100644
--- a/lib/sasl/doc/src/appup.xml
+++ b/lib/sasl/doc/src/appup.xml
@@ -137,7 +137,8 @@
code change. If it is set to <c>{advanced,Extra}</c>, implemented
processes using
<seealso marker="stdlib:gen_server"><c>gen_server</c></seealso>,
- <seealso marker="stdlib:gen_fsm"><c>gen_fsm</c></seealso>, or
+ <seealso marker="stdlib:gen_fsm"><c>gen_fsm</c></seealso>,
+ <seealso marker="stdlib:gen_statem"><c>gen_statem</c></seealso>, or
<seealso marker="stdlib:gen_event"><c>gen_event</c></seealso>
transform their internal state by calling the callback function
<c>code_change</c>. Special processes call the callback
diff --git a/lib/sasl/doc/src/error_logging.xml b/lib/sasl/doc/src/error_logging.xml
index 86d0c811e9..8464a41ff9 100644
--- a/lib/sasl/doc/src/error_logging.xml
+++ b/lib/sasl/doc/src/error_logging.xml
@@ -90,8 +90,9 @@
a process terminates with an unexpected reason, which is any reason
other than <c>normal</c>, <c>shutdown</c>, or <c>{shutdown,Term}</c>.
Processes using behaviors
- <seealso marker="stdlib:gen_server"><c>gen_server</c></seealso> or
- <seealso marker="stdlib:gen_fsm"><c>gen_fsm</c></seealso>
+ <seealso marker="stdlib:gen_server"><c>gen_server</c></seealso>,
+ <seealso marker="stdlib:gen_fsm"><c>gen_fsm</c></seealso> or
+ <seealso marker="stdlib:gen_statem"><c>gen_statem</c></seealso>
are examples of such processes. A crash report contains the following items:</p>
<taglist>
<tag><c>Crasher</c></tag>
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index ee620dcdb4..4dcaec03a7 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -54,7 +54,7 @@ unix_cases() ->
end,
[target_system, target_system_unicode] ++ RunErlCases ++ cases().
-win32_cases() ->
+win32_cases() ->
[{group,release} | cases()].
%% Cases that can be run on all platforms
@@ -89,11 +89,16 @@ groups() ->
%% {group,release}
%% Top group for all cases using run_erl
init_per_group(release, Config) ->
- Dog = ?t:timetrap(?default_timeout),
- P1gInstall = filename:join(priv_dir(Config),p1g_install),
- ok = create_p1g(Config,P1gInstall),
- ok = create_p1h(Config),
- ?t:timetrap_cancel(Dog);
+ case {os:type(), os:version()} of
+ {{win32, nt}, Vsn} when Vsn > {6,1,999999} ->
+ {skip, "Requires admin privileges on Win 8 and later"};
+ _ ->
+ Dog = ?t:timetrap(?default_timeout),
+ P1gInstall = filename:join(priv_dir(Config),p1g_install),
+ ok = create_p1g(Config,P1gInstall),
+ ok = create_p1h(Config),
+ ?t:timetrap_cancel(Dog)
+ end;
%% {group,release_single}
%% Subgroup of {group,release}, contains all cases that are not
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 850557444d..ff2d6e082a 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -241,6 +241,7 @@
{server2client,['aes128-cbc','3des-cbc']}]},
{mac,['hmac-sha2-256','hmac-sha1']},
{compression,[none,zlib]}
+ ]
}
</code>
<p>The example specifies different algorithms in the two directions (client2server and server2client),
@@ -359,7 +360,8 @@
</type>
<desc>
<p>Starts a server listening for SSH connections on the given
- port.</p>
+ port. If the <c>Port</c> is 0, a random free port is selected. See
+ <seealso marker="#daemon_info/1">daemon_info/1</seealso> about how to find the selected port number.</p>
<p>Options:</p>
<taglist>
<tag><c><![CDATA[{inet, inet | inet6}]]></c></tag>
@@ -460,6 +462,7 @@
{server2client,['aes128-cbc','3des-cbc']}]},
{mac,['hmac-sha2-256','hmac-sha1']},
{compression,[none,zlib]}
+ ]
}
</code>
<p>The example specifies different algorithms in the two directions (client2server and server2client),
@@ -680,6 +683,18 @@
</func>
<func>
+ <name>daemon_info(Daemon) -> {ok, [{port,Port}]} | {error,Error}</name>
+ <fsummary>Get info about a daemon</fsummary>
+ <type>
+ <v>Port = integer()</v>
+ <v>Error = bad_daemon_ref</v>
+ </type>
+ <desc>
+ <p>Returns a key-value list with information about the daemon. For now, only the listening port is returned. This is intended for the case the daemon is started with the port set to 0.</p>
+ </desc>
+ </func>
+
+ <func>
<name>default_algorithms() -> algs_list()</name>
<fsummary>Get a list declaring the supported algorithms</fsummary>
<desc>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index 1d37933369..071d46ec57 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -44,24 +44,41 @@
</p>
<taglist>
+ <tag><c>reason()</c></tag>
+ <item>
+ <p>= <c>atom()</c> A description of the reason why an operation failed.</p>
+ <p>
+ The value is formed from the sftp error codes in the protocol-level responses as defined in
+ <url href="https://tools.ietf.org/id/draft-ietf-secsh-filexfer-13.txt">draft-ietf-secsh-filexfer-13.txt</url>
+ section 9.1.
+ </p>
+ <p>
+ The codes are named as <c>SSH_FX_*</c> which are transformed into lowercase of the star-part.
+ E.g. the error code <c>SSH_FX_NO_SUCH_FILE</c>
+ will cause the <c>reason()</c> to be <c>no_such_file</c>.
+ </p>
+ </item>
+
<tag><c>ssh_connection_ref() =</c></tag>
- <item><p>opaque() - as returned by <c>ssh:connect/3</c></p></item>
+ <item><p><c>opaque()</c> - as returned by
+ <seealso marker="ssh#connect-3"><c>ssh:connect/3</c></seealso></p></item>
+
<tag><c>timeout()</c></tag>
- <item><p>= <c>infinity | integer() in milliseconds. Default infinity.</c></p></item>
+ <item><p>= <c>infinity | integer()</c> in milliseconds. Default infinity.</p></item>
</taglist>
</section>
<section>
<title>Time-outs</title>
<p>If the request functions for the SFTP channel return <c>{error, timeout}</c>,
- it does not guarantee that the request never reached the server and was
- not performed. It only means that no answer was received from the
- server within the expected time.</p>
+ no answer was received from the server within the expected time.</p>
+ <p>The request may have reached the server and may have been performed.
+ However, no answer was received from the server within the expected time.</p>
</section>
<funcs>
<func>
- <name>apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, Reason}</name>
+ <name>apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, reason()}</name>
<fsummary>Reads asynchronously from an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -69,16 +86,15 @@
<v>Position = integer()</v>
<v>Len = integer()</v>
<v>N = term()</v>
- <v>Reason = term()</v>
</type>
-
- <desc><p>The <c><![CDATA[apread]]></c> function reads from a specified position,
- combining the <c><![CDATA[position]]></c> and <c><![CDATA[aread]]></c> functions.</p>
- <p><seealso marker="#apread-4">ssh_sftp:apread/4</seealso></p> </desc>
+ <desc><p>The <c><![CDATA[apread/4]]></c> function reads from a specified position,
+ combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
+ <seealso marker="#aread-3"><c>aread/3</c></seealso> functions.</p>
+ </desc>
</func>
<func>
- <name>apwrite(ChannelPid, Handle, Position, Data) -> ok | {error, Reason}</name>
+ <name>apwrite(ChannelPid, Handle, Position, Data) -> {async, N} | {error, reason()}</name>
<fsummary>Writes asynchronously to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -87,16 +103,16 @@
<v>Len = integer()</v>
<v>Data = binary()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
+ <v>N = term()</v>
</type>
- <desc>
- <p><c><![CDATA[apwrite]]></c> writes on a specified position, combining
- the <c><![CDATA[position]]></c> and <c><![CDATA[awrite]]></c> operations.</p>
- <p><seealso marker="#awrite-3">ssh_sftp:awrite/3</seealso> </p></desc>
+ <desc><p>The <c><![CDATA[apwrite/4]]></c> function writes to a specified position,
+ combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
+ <seealso marker="#awrite-3"><c>awrite/3</c></seealso> functions.</p>
+ </desc>
</func>
<func>
- <name>aread(ChannelPid, Handle, Len) -> {async, N} | {error, Error}</name>
+ <name>aread(ChannelPid, Handle, Len) -> {async, N} | {error, reason()}</name>
<fsummary>Reads asynchronously from an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -104,7 +120,6 @@
<v>Position = integer()</v>
<v>Len = integer()</v>
<v>N = term()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Reads from an open file, without waiting for the result. If the
@@ -113,14 +128,12 @@
The actual data is sent as a message to the calling process. This
message has the form <c><![CDATA[{async_reply, N, Result}]]></c>, where
<c><![CDATA[Result]]></c> is the result from the read, either <c><![CDATA[{ok, Data}]]></c>,
- <c><![CDATA[eof]]></c>, or <c><![CDATA[{error, Error}]]></c>.</p>
+ <c><![CDATA[eof]]></c>, or <c><![CDATA[{error, reason()}]]></c>.</p>
</desc>
</func>
-
-
<func>
- <name>awrite(ChannelPid, Handle, Data) -> ok | {error, Reason}</name>
+ <name>awrite(ChannelPid, Handle, Data) -> {async, N} | {error, reason()}</name>
<fsummary>Writes asynchronously to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -129,7 +142,6 @@
<v>Len = integer()</v>
<v>Data = binary()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Writes to an open file, without waiting for the result. If the
@@ -138,19 +150,18 @@
<c><![CDATA[awrite]]></c>. The result of the <c><![CDATA[write]]></c> operation is sent
as a message to the calling process. This message has the form
<c><![CDATA[{async_reply, N, Result}]]></c>, where <c><![CDATA[Result]]></c> is the result
- from the write, either <c><![CDATA[ok]]></c>, or <c><![CDATA[{error, Error}]]></c>.</p>
+ from the write, either <c><![CDATA[ok]]></c>, or <c><![CDATA[{error, reason()}]]></c>.</p>
</desc>
</func>
<func>
<name>close(ChannelPid, Handle) -></name>
- <name>close(ChannelPid, Handle, Timeout) -> ok | {error, Reason}</name>
+ <name>close(ChannelPid, Handle, Timeout) -> ok | {error, reason()}</name>
<fsummary>Closes an open handle.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Handle = term()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Closes a handle to an open file or directory on the server.</p>
@@ -159,29 +170,27 @@
<func>
<name>delete(ChannelPid, Name) -></name>
- <name>delete(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
+ <name>delete(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
<fsummary>Deletes a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
- <p>Deletes the file specified by <c><![CDATA[Name]]></c>, like
- <seealso marker="kernel:file#delete-1">file:delete/1</seealso></p>
+ <p>Deletes the file specified by <c><![CDATA[Name]]></c>.
+ </p>
</desc>
</func>
<func>
<name>del_dir(ChannelPid, Name) -></name>
- <name>del_dir(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
+ <name>del_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
<fsummary>Deletes an empty directory.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Deletes a directory specified by <c><![CDATA[Name]]></c>.
@@ -192,7 +201,7 @@
<func>
<name>list_dir(ChannelPid, Path) -></name>
- <name>list_dir(ChannelPid, Path, Timeout) -> {ok, Filenames} | {error, Reason}</name>
+ <name>list_dir(ChannelPid, Path, Timeout) -> {ok, Filenames} | {error, reason()}</name>
<fsummary>Lists the directory.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -200,7 +209,6 @@
<v>Filenames = [Filename]</v>
<v>Filename = string()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Lists the given directory on the server, returning the
@@ -210,13 +218,12 @@
<func>
<name>make_dir(ChannelPid, Name) -></name>
- <name>make_dir(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
+ <name>make_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
<fsummary>Creates a directory.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Creates a directory specified by <c><![CDATA[Name]]></c>. <c><![CDATA[Name]]></c>
@@ -227,24 +234,23 @@
<func>
<name>make_symlink(ChannelPid, Name, Target) -></name>
- <name>make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, Reason}</name>
+ <name>make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, reason()}</name>
<fsummary>Creates a symbolic link.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
<v>Target = string()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Creates a symbolic link pointing to <c><![CDATA[Target]]></c> with the
- name <c><![CDATA[Name]]></c>, like
- <seealso marker="kernel:file#make_symlink-2">file:make_symlink/2</seealso></p>
+ name <c><![CDATA[Name]]></c>.
+ </p>
</desc>
</func>
<func>
<name>open(ChannelPid, File, Mode) -></name>
- <name>open(ChannelPid, File, Mode, Timeout) -> {ok, Handle} | {error, Reason}</name>
+ <name>open(ChannelPid, File, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name>
<fsummary>Opens a file and returns a handle.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -253,7 +259,6 @@
<v>Modeflag = read | write | creat | trunc | append | binary</v>
<v>Timeout = timeout()</v>
<v>Handle = term()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Opens a file on the server and returns a handle, which
@@ -262,13 +267,12 @@
</func>
<func>
<name>opendir(ChannelPid, Path) -></name>
- <name>opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | {error, Reason}</name>
+ <name>opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | {error, reason()}</name>
<fsummary>Opens a directory and returns a handle.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Path = string()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Opens a handle to a directory on the server. The handle
@@ -278,7 +282,7 @@
<func>
<name>open_tar(ChannelPid, Path, Mode) -></name>
- <name>open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | {error, Reason}</name>
+ <name>open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name>
<fsummary>Opens a tar file on the server to which <c>ChannelPid</c>
is connected and returns a handle.</fsummary>
<type>
@@ -298,7 +302,6 @@
<v>DecryptResult = {ok,PlainBin,CryptoState} | {ok,PlainBin,CryptoState,ChunkSize}</v>
<v>CloseFun = (fun(PlainBin,CryptoState) -> {ok,EncryptedBin})</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Opens a handle to a tar file on the server, associated with <c>ChannelPid</c>.
@@ -333,7 +336,7 @@
<func>
<name>position(ChannelPid, Handle, Location) -></name>
- <name>position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition} | {error, Reason}</name>
+ <name>position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, reason()}</name>
<fsummary>Sets the file position of a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -343,12 +346,11 @@
<v>Offset = integer()</v>
<v>Timeout = timeout()</v>
<v>NewPosition = integer()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>.
Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if
- successful, otherwise <c><![CDATA[{error, Reason}]]></c>. <c><![CDATA[Location]]></c> is
+ successful, otherwise <c><![CDATA[{error, reason()}]]></c>. <c><![CDATA[Location]]></c> is
one of the following:</p>
<taglist>
<tag><c><![CDATA[Offset]]></c></tag>
@@ -379,7 +381,7 @@
<func>
<name>pread(ChannelPid, Handle, Position, Len) -></name>
- <name>pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | {error, Error}</name>
+ <name>pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name>
<fsummary>Reads from an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -388,18 +390,16 @@
<v>Len = integer()</v>
<v>Timeout = timeout()</v>
<v>Data = string() | binary()</v>
- <v>Reason = term()</v>
</type>
- <desc>
- <p>The <c><![CDATA[pread]]></c> function reads from a specified position,
- combining the <c><![CDATA[position]]></c> and <c><![CDATA[read]]></c> functions.</p>
- <p><seealso marker="#read-4">ssh_sftp:read/4</seealso></p>
- </desc>
- </func>
+ <desc><p>The <c><![CDATA[pread/3,4]]></c> function reads from a specified position,
+ combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
+ <seealso marker="#read-3"><c>read/3,4</c></seealso> functions.</p>
+ </desc>
+ </func>
<func>
<name>pwrite(ChannelPid, Handle, Position, Data) -> ok</name>
- <name>pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, Reason}</name>
+ <name>pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, reason()}</name>
<fsummary>Writes to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -407,19 +407,16 @@
<v>Position = integer()</v>
<v>Data = iolist()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
- <desc>
- <p>The <c><![CDATA[pread]]></c> function writes to a specified position,
- combining the <c><![CDATA[position]]></c> and <c><![CDATA[write]]></c> functions.</p>
- <p><seealso marker="#write-3">ssh_sftp:write/3</seealso></p>
- </desc>
+ <desc><p>The <c><![CDATA[pwrite/3,4]]></c> function writes to a specified position,
+ combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
+ <seealso marker="#write-3"><c>write/3,4</c></seealso> functions.</p>
+ </desc>
</func>
-
- <func>
+ <func>
<name>read(ChannelPid, Handle, Len) -></name>
- <name>read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, Error}</name>
+ <name>read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name>
<fsummary>Reads from an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -428,12 +425,11 @@
<v>Len = integer()</v>
<v>Timeout = timeout()</v>
<v>Data = string() | binary()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by
<c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or
- <c><![CDATA[{error, Reason}]]></c>. If the file is opened with <c><![CDATA[binary]]></c>,
+ <c><![CDATA[{error, reason()}]]></c>. If the file is opened with <c><![CDATA[binary]]></c>,
<c><![CDATA[Data]]></c> is a binary, otherwise it is a string.</p>
<p>If the file is read past <c>eof</c>, only the remaining bytes
are read and returned. If no bytes are read, <c><![CDATA[eof]]></c>
@@ -443,25 +439,22 @@
<func>
<name>read_file(ChannelPid, File) -></name>
- <name>read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, Reason}</name>
+ <name>read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, reason()}</name>
<fsummary>Reads a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>File = string()</v>
<v>Data = binary()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
- <p>Reads a file from the server, and returns the data in a binary,
- like
- <seealso marker="kernel:file#read_file-1">file:read_file/1</seealso></p>
+ <p>Reads a file from the server, and returns the data in a binary.</p>
</desc>
</func>
<func>
<name>read_file_info(ChannelPid, Name) -></name>
- <name>read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, Reason}</name>
+ <name>read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name>
<fsummary>Gets information about a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -469,35 +462,34 @@
<v>Handle = term()</v>
<v>Timeout = timeout()</v>
<v>FileInfo = record()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Returns a <c><![CDATA[file_info]]></c> record from the file specified by
- <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>,
- like <seealso marker="kernel:file#read_file_info-2">file:read_file_info/2</seealso></p>
+ <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>. See
+ <seealso marker="kernel:file#read_file_info-2">file:read_file_info/2</seealso>
+ for information about the record.
+ </p>
</desc>
</func>
<func>
<name>read_link(ChannelPid, Name) -></name>
- <name>read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, Reason}</name>
+ <name>read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, reason()}</name>
<fsummary>Reads symbolic link.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
<v>Target = string()</v>
- <v>Reason = term()</v>
</type>
<desc>
- <p>Reads the link target from the symbolic link specified
- by <c><![CDATA[name]]></c>, like
- <seealso marker="kernel:file#read_link-1">file:read_link/1</seealso></p>
+ <p>Reads the link target from the symbolic link specified by <c><![CDATA[name]]></c>.
+ </p>
</desc>
</func>
<func>
- <name>read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, Reason}</name>
- <name>read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, Reason}</name>
+ <name>read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, reason()}</name>
+ <name>read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name>
<fsummary>Gets information about a symbolic link.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -505,30 +497,31 @@
<v>Handle = term()</v>
<v>Timeout = timeout()</v>
<v>FileInfo = record()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Returns a <c><![CDATA[file_info]]></c> record from the symbolic
- link specified by <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>, like
- <seealso marker="kernel:file#read_link_info-2">file:read_link_info/2</seealso></p>
+ link specified by <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>.
+ See
+ <seealso marker="kernel:file#read_link_info-2">file:read_link_info/2</seealso>
+ for information about the record.
+ </p>
</desc>
</func>
<func>
<name>rename(ChannelPid, OldName, NewName) -> </name>
- <name>rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, Reason}</name>
+ <name>rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, reason()}</name>
<fsummary>Renames a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>OldName = string()</v>
<v>NewName = string()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Renames a file named <c><![CDATA[OldName]]></c> and gives it the name
- <c><![CDATA[NewName]]></c>, like
- <seealso marker="kernel:file#rename-2">file:rename/2</seealso></p>
+ <c><![CDATA[NewName]]></c>.
+ </p>
</desc>
</func>
@@ -537,14 +530,13 @@
<name>start_channel(ConnectionRef, Options) -></name>
<name>start_channel(Host, Options) -></name>
<name>start_channel(Host, Port, Options) -> {ok, Pid} | {ok, Pid, ConnectionRef} |
- {error, Reason}</name>
+ {error, reason()|term()}</name>
<fsummary>Starts an SFTP client.</fsummary>
<type>
<v>Host = string()</v>
<v>ConnectionRef = ssh_connection_ref()</v>
<v>Port = integer()</v>
<v>Options = [{Option, Value}]</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>If no connection reference is provided, a connection is set
@@ -592,7 +584,7 @@
<func>
<name>write(ChannelPid, Handle, Data) -></name>
- <name>write(ChannelPid, Handle, Data, Timeout) -> ok | {error, Reason}</name>
+ <name>write(ChannelPid, Handle, Data, Timeout) -> ok | {error, reason()}</name>
<fsummary>Writes to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -600,61 +592,47 @@
<v>Position = integer()</v>
<v>Data = iolist()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Writes <c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>.
The file is to be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c>
- flag. Returns <c><![CDATA[ok]]></c> if successful or <c><![CDATA[{error, Reason}]]></c>
+ flag. Returns <c><![CDATA[ok]]></c> if successful or <c><![CDATA[{error, reason()}]]></c>
otherwise.</p>
- <p>Typical error reasons:</p>
- <taglist>
- <tag><c><![CDATA[ebadf]]></c></tag>
- <item>
- <p>File is not opened for writing.</p>
- </item>
- <tag><c><![CDATA[enospc]]></c></tag>
- <item>
- <p>No space is left on the device.</p>
- </item>
- </taglist>
</desc>
</func>
<func>
<name>write_file(ChannelPid, File, Iolist) -></name>
- <name>write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, Reason}</name>
+ <name>write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, reason()}</name>
<fsummary>Writes a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>File = string()</v>
<v>Iolist = iolist()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
- <p>Writes a file to the server, like <seealso
- marker="kernel:file#write_file-2">file:write_file/2</seealso> The
- file is created if it does not exist. The file is overwritten
- if it exists.</p>
+ <p>Writes a file to the server. The file is created if it does not exist
+ but overwritten if it exists.</p>
</desc>
</func>
<func>
<name>write_file_info(ChannelPid, Name, Info) -></name>
- <name>write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, Reason}</name>
+ <name>write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, reason()}</name>
<fsummary>Writes information for a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
<v>Info = record()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
<p>Writes file information from a <c><![CDATA[file_info]]></c> record to the
- file specified by <c><![CDATA[Name]]></c>, like
- <seealso marker="kernel:file#write_file_info-2">file:write_file_info/[2,3]</seealso></p>
+ file specified by <c><![CDATA[Name]]></c>. See
+ <seealso marker="kernel:file#write_file_info-2">file:write_file_info/[2,3]</seealso>
+ for information about the record.
+ </p>
</desc>
</func>
</funcs>
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index c67350bf72..3245ba5197 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -40,7 +40,12 @@
{applications, [kernel, stdlib, crypto, public_key]},
{env, []},
{mod, {ssh_app, []}},
- {runtime_dependencies, ["stdlib-2.3","public_key-0.22","kernel-3.0",
- "erts-6.0","crypto-3.3"]}]}.
+ {runtime_dependencies, [
+ "crypto-3.3",
+ "erts-6.0",
+ "kernel-3.0",
+ "public_key-1.1",
+ "stdlib-3.0"
+ ]}]}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index d0121e73ba..09b07b7a2a 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -30,12 +30,18 @@
-export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2,
channel_info/3,
daemon/1, daemon/2, daemon/3,
+ daemon_info/1,
default_algorithms/0,
stop_listener/1, stop_listener/2, stop_listener/3,
stop_daemon/1, stop_daemon/2, stop_daemon/3,
shell/1, shell/2, shell/3
]).
+%%% Type exports
+-export_type([connection_ref/0,
+ channel_id/0
+ ]).
+
%%--------------------------------------------------------------------
-spec start() -> ok | {error, term()}.
-spec start(permanent | transient | temporary) -> ok | {error, term()}.
@@ -81,7 +87,7 @@ connect(Host, Port, Options, Timeout) ->
ConnectionTimeout = proplists:get_value(connect_timeout, Options, infinity),
try Transport:connect(Host, Port, [ {active, false} | SocketOptions], ConnectionTimeout) of
{ok, Socket} ->
- Opts = [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)],
+ Opts = [{user_pid,self()}, {host,Host} | SshOptions],
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
{error, Reason} ->
{error, Reason}
@@ -153,6 +159,19 @@ daemon(HostAddr, Port, Options0) ->
start_daemon(Host, Port, Options, Inet).
%%--------------------------------------------------------------------
+daemon_info(Pid) ->
+ case catch ssh_system_sup:acceptor_supervisor(Pid) of
+ AsupPid when is_pid(AsupPid) ->
+ [Port] =
+ [Prt || {{ssh_acceptor_sup,any,Prt,default},
+ _WorkerPid,worker,[ssh_acceptor]} <- supervisor:which_children(AsupPid)],
+ {ok, [{port,Port}]};
+
+ _ ->
+ {error,bad_daemon_ref}
+ end.
+
+%%--------------------------------------------------------------------
-spec stop_listener(pid()) -> ok.
-spec stop_listener(inet:ip_address(), integer()) -> ok.
%%
@@ -223,13 +242,6 @@ default_algorithms() ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-fix_idle_time(SshOptions) ->
- case proplists:get_value(idle_time, SshOptions) of
- undefined ->
- [{idle_time, infinity}|SshOptions];
- _ ->
- SshOptions
- end.
start_daemon(Host, Port, Options, Inet) ->
case handle_options(Options) of
{error, _Reason} = Error ->
@@ -243,32 +255,52 @@ start_daemon(Host, Port, Options, Inet) ->
end
end.
-do_start_daemon(Host0, Port0, Options, SocketOptions) ->
- {Host,Port} = try
- case proplists:get_value(fd, SocketOptions) of
- undefined ->
- {Host0,Port0};
- Fd when Port0==0 ->
- find_hostport(Fd);
- _ ->
- {Host0,Port0}
- end
- catch
- _:_ -> throw(bad_fd)
- end,
- Profile = proplists:get_value(profile, Options, ?DEFAULT_PROFILE),
+do_start_daemon(Host0, Port0, SshOptions, SocketOptions) ->
+ {Host,Port1} =
+ try
+ case proplists:get_value(fd, SocketOptions) of
+ undefined ->
+ {Host0,Port0};
+ Fd when Port0==0 ->
+ find_hostport(Fd);
+ _ ->
+ {Host0,Port0}
+ end
+ catch
+ _:_ -> throw(bad_fd)
+ end,
+ Profile = proplists:get_value(profile, SshOptions, ?DEFAULT_PROFILE),
+ {Port, WaitRequestControl, Opts} =
+ case Port1 of
+ 0 -> %% Allocate the socket here to get the port number...
+ {_, Callback, _} =
+ proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}),
+ {ok,LSock} = ssh_acceptor:callback_listen(Callback, 0, SocketOptions),
+ {ok,{_,LPort}} = inet:sockname(LSock),
+ {LPort,
+ {LSock,Callback},
+ [{lsocket,LSock},{lsock_owner,self()}]
+ };
+ _ ->
+ {Port1, false, []}
+ end,
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
undefined ->
%% It would proably make more sense to call the
%% address option host but that is a too big change at the
%% monent. The name is a legacy name!
try sshd_sup:start_child([{address, Host},
- {port, Port}, {role, server},
+ {port, Port},
+ {role, server},
{socket_opts, SocketOptions},
- {ssh_opts, Options}]) of
+ {ssh_opts, SshOptions}
+ | Opts]) of
{error, {already_started, _}} ->
{error, eaddrinuse};
- Result = {Code, _} when (Code == ok) or (Code == error) ->
+ Result = {ok,_} ->
+ sync_request_control(WaitRequestControl),
+ Result;
+ Result = {error, _} ->
Result
catch
exit:{noproc, _} ->
@@ -277,18 +309,31 @@ do_start_daemon(Host0, Port0, Options, SocketOptions) ->
Sup ->
AccPid = ssh_system_sup:acceptor_supervisor(Sup),
case ssh_acceptor_sup:start_child(AccPid, [{address, Host},
- {port, Port}, {role, server},
+ {port, Port},
+ {role, server},
{socket_opts, SocketOptions},
- {ssh_opts, Options}]) of
+ {ssh_opts, SshOptions}
+ | Opts]) of
{error, {already_started, _}} ->
{error, eaddrinuse};
{ok, _} ->
+ sync_request_control(WaitRequestControl),
{ok, Sup};
Other ->
Other
end
end.
+sync_request_control(false) ->
+ ok;
+sync_request_control({LSock,Callback}) ->
+ receive
+ {request_control,LSock,ReqPid} ->
+ ok = Callback:controlling_process(LSock, ReqPid),
+ ReqPid ! {its_yours,LSock},
+ ok
+ end.
+
find_hostport(Fd) ->
%% Using internal functions inet:open/8 and inet:close/0.
%% Don't try this at home unless you know what you are doing!
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 73d6e4d2bc..868f3a9181 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -70,8 +70,6 @@
-record(ssh,
{
- %%state, %% what it's waiting for
-
role, %% client | server
peer, %% string version of peer address
@@ -135,8 +133,8 @@
user,
service,
userauth_quiet_mode, % boolean()
- userauth_supported_methods, % string() eg "keyboard-interactive,password"
userauth_methods, % list( string() ) eg ["keyboard-interactive", "password"]
+ userauth_supported_methods, % string() eg "keyboard-interactive,password"
kb_tries_left = 0, % integer(), num tries left for "keyboard-interactive"
userauth_preference,
available_host_keys,
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index d94dedf1bf..90fd951dcd 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -26,7 +26,8 @@
%% Internal application API
-export([start_link/5,
- number_of_connections/1]).
+ number_of_connections/1,
+ callback_listen/3]).
%% spawn export
-export([acceptor_init/6, acceptor_loop/6]).
@@ -46,15 +47,39 @@ start_link(Port, Address, SockOpts, Opts, AcceptTimeout) ->
acceptor_init(Parent, Port, Address, SockOpts, Opts, AcceptTimeout) ->
{_, Callback, _} =
proplists:get_value(transport, Opts, {tcp, gen_tcp, tcp_closed}),
- case (catch do_socket_listen(Callback, Port, [{active, false} | SockOpts])) of
- {ok, ListenSocket} ->
+
+ SockOwner = proplists:get_value(lsock_owner, Opts),
+ LSock = proplists:get_value(lsocket, Opts),
+ UseExistingSocket =
+ case catch inet:sockname(LSock) of
+ {ok,{_,Port}} -> is_pid(SockOwner);
+ _ -> false
+ end,
+
+ case UseExistingSocket of
+ true ->
proc_lib:init_ack(Parent, {ok, self()}),
- acceptor_loop(Callback,
- Port, Address, Opts, ListenSocket, AcceptTimeout);
- Error ->
- proc_lib:init_ack(Parent, Error),
- error
+ request_ownership(LSock, SockOwner),
+ acceptor_loop(Callback, Port, Address, Opts, LSock, AcceptTimeout);
+
+ false ->
+ case (catch do_socket_listen(Callback, Port, SockOpts)) of
+ {ok, ListenSocket} ->
+ proc_lib:init_ack(Parent, {ok, self()}),
+ acceptor_loop(Callback,
+ Port, Address, Opts, ListenSocket, AcceptTimeout);
+ Error ->
+ proc_lib:init_ack(Parent, Error),
+ error
+ end
end.
+
+request_ownership(LSock, SockOwner) ->
+ SockOwner ! {request_control,LSock,self()},
+ receive
+ {its_yours,LSock} -> ok
+ end.
+
do_socket_listen(Callback, Port0, Opts) ->
Port =
@@ -62,6 +87,10 @@ do_socket_listen(Callback, Port0, Opts) ->
undefined -> Port0;
_ -> 0
end,
+ callback_listen(Callback, Port, Opts).
+
+callback_listen(Callback, Port, Opts0) ->
+ Opts = [{active, false}, {reuseaddr,true} | Opts0],
case Callback:listen(Port, Opts) of
{error, nxdomain} ->
Callback:listen(Port, lists:delete(inet6, Opts));
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index b2f489a971..4f76dbe6f0 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -85,10 +85,7 @@ child_spec(ServerOpts) ->
Profile = proplists:get_value(profile, proplists:get_value(ssh_opts, ServerOpts), ?DEFAULT_PROFILE),
Name = id(Address, Port, Profile),
SocketOpts = proplists:get_value(socket_opts, ServerOpts),
- StartFunc = {ssh_acceptor, start_link, [Port, Address,
- [{active, false},
- {reuseaddr, true}] ++ SocketOpts,
- ServerOpts, Timeout]},
+ StartFunc = {ssh_acceptor, start_link, [Port, Address, SocketOpts, ServerOpts, Timeout]},
Restart = transient,
Shutdown = brutal_kill,
Modules = [ssh_acceptor],
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 4b3c21ce3f..49eec8072f 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -135,9 +135,9 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
service = "ssh-connection"});
{error, no_user} ->
ErrStr = "Could not determine the users name",
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_ILLEGAL_USER_NAME,
- description = ErrStr,
- language = "en"})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_ILLEGAL_USER_NAME,
+ description = ErrStr})
end.
userauth_request_msg(#ssh{userauth_preference = []} = Ssh) ->
@@ -355,10 +355,10 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = "Server does not support"
- "keyboard-interactive",
- language = "en"}).
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "Server does not support keyboard-interactive"
+ }).
%%--------------------------------------------------------------------
@@ -420,10 +420,10 @@ check_password(User, Password, Opts, Ssh) ->
{false,NewState} ->
{false, Ssh#ssh{pwdfun_user_state=NewState}};
disconnect ->
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description =
- "Unable to connect using the available authentication methods",
- language = ""})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "Unable to connect using the available authentication methods"
+ })
end
end.
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index de6908bb38..a8e6ebde16 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -68,7 +68,7 @@
%% Internal application API
-export([cache_create/0, cache_lookup/2, cache_update/2,
cache_delete/1, cache_delete/2, cache_foldl/3,
- cache_find/2,
+ cache_info/2, cache_find/2,
get_print_info/1]).
-record(state, {
@@ -335,6 +335,9 @@ cache_delete(Cache) ->
cache_foldl(Fun, Acc, Cache) ->
ets:foldl(Fun, Acc, Cache).
+cache_info(num_entries, Cache) ->
+ proplists:get_value(size, ets:info(Cache)).
+
cache_find(ChannelPid, Cache) ->
case ets:match_object(Cache, #channel{user = ChannelPid}) of
[] ->
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 397d51de9d..4fb6bc39f3 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -22,13 +22,15 @@
%%% Description : SSH connection protocol
--type channel_id() :: integer().
+-type role() :: client | server .
+-type connection_ref() :: pid().
+-type channel_id() :: pos_integer().
-define(DEFAULT_PACKET_SIZE, 65536).
-define(DEFAULT_WINDOW_SIZE, 10*?DEFAULT_PACKET_SIZE).
-define(DEFAULT_TIMEOUT, 5000).
--define(MAX_PROTO_VERSION, 255).
+-define(MAX_PROTO_VERSION, 255). % Max length of the hello string
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -241,7 +243,7 @@
-record(channel,
{
- type, %% "session", "x11", "forwarded-tcpip", "direct-tcpip"
+ type, %% "session"
sys, %% "none", "shell", "exec" "subsystem"
user, %% "user" process id (default to cm user)
flow_control,
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index a34478732c..d0f2d54c06 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -38,8 +38,7 @@
%% Potential API currently unsupported and not tested
-export([window_change/4, window_change/6,
- direct_tcpip/6, direct_tcpip/8, tcpip_forward/3,
- cancel_tcpip_forward/3, signal/3, exit_status/3]).
+ signal/3, exit_status/3]).
%% Internal application API
-export([channel_data/5, handle_msg/3, channel_eof_msg/1,
@@ -48,7 +47,7 @@
channel_adjust_window_msg/2, channel_data_msg/3,
channel_open_msg/5, channel_open_confirmation_msg/4,
channel_open_failure_msg/4, channel_request_msg/4,
- global_request_msg/3, request_failure_msg/0,
+ request_failure_msg/0,
request_success_msg/1, bind/4, unbind/3, unbind_channel/2,
bound_channel/3, encode_ip/1]).
@@ -232,52 +231,6 @@ exit_status(ConnectionHandler, Channel, Status) ->
ssh_connection_handler:request(ConnectionHandler, Channel,
"exit-status", false, [?uint32(Status)], 0).
-direct_tcpip(ConnectionHandler, RemoteHost,
- RemotePort, OrigIP, OrigPort, Timeout) ->
- direct_tcpip(ConnectionHandler, RemoteHost, RemotePort, OrigIP, OrigPort,
- ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout).
-
-direct_tcpip(ConnectionHandler, RemoteIP, RemotePort, OrigIP, OrigPort,
- InitialWindowSize, MaxPacketSize, Timeout) ->
- case {encode_ip(RemoteIP), encode_ip(OrigIP)} of
- {false, _} ->
- {error, einval};
- {_, false} ->
- {error, einval};
- {RIP, OIP} ->
- ssh_connection_handler:open_channel(ConnectionHandler,
- "direct-tcpip",
- [?string(RIP),
- ?uint32(RemotePort),
- ?string(OIP),
- ?uint32(OrigPort)],
- InitialWindowSize,
- MaxPacketSize,
- Timeout)
- end.
-
-tcpip_forward(ConnectionHandler, BindIP, BindPort) ->
- case encode_ip(BindIP) of
- false ->
- {error, einval};
- IPStr ->
- ssh_connection_handler:global_request(ConnectionHandler,
- "tcpip-forward", true,
- [?string(IPStr),
- ?uint32(BindPort)])
- end.
-
-cancel_tcpip_forward(ConnectionHandler, BindIP, Port) ->
- case encode_ip(BindIP) of
- false ->
- {error, einval};
- IPStr ->
- ssh_connection_handler:global_request(ConnectionHandler,
- "cancel-tcpip-forward", true,
- [?string(IPStr),
- ?uint32(Port)])
- end.
-
%%--------------------------------------------------------------------
%%% Internal API
%%--------------------------------------------------------------------
@@ -300,22 +253,11 @@ l2b([]) ->
channel_data(ChannelId, DataType, Data, Connection, From)
when is_list(Data)->
- channel_data(ChannelId, DataType,
-%% list_to_binary(Data), Connection, From);
- l2b(Data), Connection, From);
- %% try list_to_binary(Data)
- %% of
- %% B -> B
- %% catch
- %% _:_ -> io:format('BAD BINARY: ~p~n',[Data]),
- %% unicode:characters_to_binary(Data)
- %% end,
- %% Connection, From);
+ channel_data(ChannelId, DataType, l2b(Data), Connection, From);
channel_data(ChannelId, DataType, Data,
#connection{channel_cache = Cache} = Connection,
From) ->
-
case ssh_channel:cache_lookup(Cache, ChannelId) of
#channel{remote_id = Id, sent_close = false} = Channel0 ->
{SendList, Channel} =
@@ -331,8 +273,7 @@ channel_data(ChannelId, DataType, Data,
FlowCtrlMsgs = flow_control(Replies, Channel, Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
_ ->
- gen_fsm:reply(From, {error, closed}),
- {noreply, Connection}
+ {{replies,[{channel_request_reply,From,{error,closed}}]}, Connection}
end.
handle_msg(#ssh_msg_channel_open_confirmation{recipient_channel = ChannelId,
@@ -499,7 +440,8 @@ handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
handle_msg(#ssh_msg_channel_open{channel_type = "session",
sender_channel = RemoteId},
- Connection, client) ->
+ Connection,
+ client) ->
%% Client implementations SHOULD reject any session channel open
%% requests to make it more difficult for a corrupt server to attack the
%% client. See See RFC 4254 6.1.
@@ -509,73 +451,6 @@ handle_msg(#ssh_msg_channel_open{channel_type = "session",
{{replies, [{connection_reply, FailMsg}]},
Connection};
-handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip" = Type,
- sender_channel = RemoteId,
- initial_window_size = RWindowSz,
- maximum_packet_size = RPacketSz,
- data = Data},
- #connection{channel_cache = Cache,
- options = SSHopts} = Connection0, server) ->
- <<?UINT32(ALen), Address:ALen/binary, ?UINT32(Port),
- ?UINT32(OLen), Orig:OLen/binary, ?UINT32(OrigPort)>> = Data,
-
- MinAcceptedPackSz = proplists:get_value(minimal_remote_max_packet_size, SSHopts, 0),
-
- if
- MinAcceptedPackSz =< RPacketSz ->
- case bound_channel(Address, Port, Connection0) of
- undefined ->
- FailMsg = channel_open_failure_msg(RemoteId,
- ?SSH_OPEN_CONNECT_FAILED,
- "Connection refused", "en"),
- {{replies,
- [{connection_reply, FailMsg}]}, Connection0};
- ChannelPid ->
- {ChannelId, Connection1} = new_channel_id(Connection0),
- LWindowSz = ?DEFAULT_WINDOW_SIZE,
- LPacketSz = ?DEFAULT_PACKET_SIZE,
- Channel = #channel{type = Type,
- sys = "none",
- user = ChannelPid,
- local_id = ChannelId,
- recv_window_size = LWindowSz,
- recv_packet_size = LPacketSz,
- send_window_size = RWindowSz,
- send_packet_size = RPacketSz,
- send_buf = queue:new()
- },
- ssh_channel:cache_update(Cache, Channel),
- OpenConfMsg = channel_open_confirmation_msg(RemoteId, ChannelId,
- LWindowSz, LPacketSz),
- {OpenMsg, Connection} =
- reply_msg(Channel, Connection1,
- {open, Channel, {forwarded_tcpip,
- decode_ip(Address), Port,
- decode_ip(Orig), OrigPort}}),
- {{replies, [{connection_reply, OpenConfMsg},
- OpenMsg]}, Connection}
- end;
-
- MinAcceptedPackSz > RPacketSz ->
- FailMsg = channel_open_failure_msg(RemoteId,
- ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
- lists:concat(["Maximum packet size below ",MinAcceptedPackSz,
- " not supported"]), "en"),
- {{replies, [{connection_reply, FailMsg}]}, Connection0}
- end;
-
-
-handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
- sender_channel = RemoteId},
- Connection, client) ->
- %% Client implementations SHOULD reject direct TCP/IP open requests for
- %% security reasons. See RFC 4254 7.2.
- FailMsg = channel_open_failure_msg(RemoteId,
- ?SSH_OPEN_CONNECT_FAILED,
- "Connection refused", "en"),
- {{replies, [{connection_reply, FailMsg}]}, Connection};
-
-
handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection, _) ->
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
@@ -786,11 +661,11 @@ handle_msg(#ssh_msg_global_request{name = _Type,
handle_msg(#ssh_msg_request_failure{},
#connection{requests = [{_, From} | Rest]} = Connection, _) ->
- {{replies, [{channel_requst_reply, From, {failure, <<>>}}]},
+ {{replies, [{channel_request_reply, From, {failure, <<>>}}]},
Connection#connection{requests = Rest}};
handle_msg(#ssh_msg_request_success{data = Data},
#connection{requests = [{_, From} | Rest]} = Connection, _) ->
- {{replies, [{channel_requst_reply, From, {success, Data}}]},
+ {{replies, [{channel_request_reply, From, {success, Data}}]},
Connection#connection{requests = Rest}};
handle_msg(#ssh_msg_disconnect{code = Code,
@@ -886,10 +761,6 @@ channel_request_msg(ChannelId, Type, WantReply, Data) ->
want_reply = WantReply,
data = Data}.
-global_request_msg(Type, WantReply, Data) ->
- #ssh_msg_global_request{name = Type,
- want_reply = WantReply,
- data = Data}.
request_failure_msg() ->
#ssh_msg_request_failure{}.
@@ -1059,7 +930,7 @@ request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid},
Connection, Reply) ->
case lists:keysearch(ChannelId, 1, Requests) of
{value, {ChannelId, From}} ->
- {{channel_requst_reply, From, Reply},
+ {{channel_request_reply, From, Reply},
Connection#connection{requests =
lists:keydelete(ChannelId, 1, Requests)}};
false when (Reply == success) or (Reply == failure) ->
@@ -1351,11 +1222,6 @@ decode_pty_opts2(<<Code, ?UINT32(Value), Tail/binary>>) ->
end,
[{Op, Value} | decode_pty_opts2(Tail)].
-decode_ip(Addr) when is_binary(Addr) ->
- case inet_parse:address(binary_to_list(Addr)) of
- {error,_} -> Addr;
- {ok,A} -> A
- end.
backwards_compatible([], Acc) ->
Acc;
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 2bef6a41cd..946adcf384 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -28,94 +28,90 @@
-module(ssh_connection_handler).
--behaviour(gen_fsm).
+-behaviour(gen_statem).
-include("ssh.hrl").
-include("ssh_transport.hrl").
-include("ssh_auth.hrl").
-include("ssh_connect.hrl").
--compile(export_all).
--export([start_link/3]).
-%% Internal application API
--export([open_channel/6, reply_request/3, request/6, request/7,
- global_request/4, send/5, send_eof/2, info/1, info/2,
- connection_info/2, channel_info/3,
- adjust_window/3, close/2, stop/1, renegotiate/1, renegotiate_data/1,
- start_connection/4,
- get_print_info/1]).
-
-%% gen_fsm callbacks
--export([hello/2, kexinit/2, key_exchange/2,
- key_exchange_dh_gex_init/2, key_exchange_dh_gex_reply/2,
- new_keys/2,
- service_request/2, connected/2,
- userauth/2,
- userauth_keyboard_interactive/2,
- userauth_keyboard_interactive_info_response/2,
- error/2]).
-
--export([init/1, handle_event/3,
- handle_sync_event/4, handle_info/3, terminate/3, format_status/2, code_change/4]).
-
--record(state, {
- role,
- client,
- starter,
- auth_user,
- connection_state,
- latest_channel_id = 0,
- idle_timer_ref,
- transport_protocol, % ex: tcp
- transport_cb,
- transport_close_tag,
- ssh_params, % #ssh{} - from ssh.hrl
- socket, % socket()
- decoded_data_buffer, % binary()
- encoded_data_buffer, % binary()
- undecoded_packet_length, % integer()
- key_exchange_init_msg, % #ssh_msg_kexinit{}
- renegotiate = false, % boolean()
- last_size_rekey = 0,
- event_queue = [],
- connection_queue,
- address,
- port,
- opts,
- recbuf
- }).
-
--type state_name() :: hello | kexinit | key_exchange | key_exchange_dh_gex_init |
- key_exchange_dh_gex_reply | new_keys | service_request |
- userauth | userauth_keyboard_interactive |
- userauth_keyboard_interactive_info_response |
- connection.
-
--type gen_fsm_state_return() :: {next_state, state_name(), term()} |
- {next_state, state_name(), term(), timeout()} |
- {stop, term(), term()}.
-
--type gen_fsm_sync_return() :: {next_state, state_name(), term()} |
- {next_state, state_name(), term(), timeout()} |
- {reply, term(), state_name(), term()} |
- {stop, term(), term(), term()}.
+%%====================================================================
+%%% Exports
+%%====================================================================
+
+%%% Start and stop
+-export([start_link/3,
+ stop/1
+ ]).
+
+%%% Internal application API
+-export([start_connection/4,
+ open_channel/6,
+ request/6, request/7,
+ reply_request/3,
+ send/5,
+ send_eof/2,
+ info/1, info/2,
+ connection_info/2,
+ channel_info/3,
+ adjust_window/3, close/2,
+ disconnect/1, disconnect/2,
+ get_print_info/1
+ ]).
+
+%%% Behaviour callbacks
+-export([handle_event/4, terminate/3, format_status/2, code_change/4]).
+
+%%% Exports not intended to be used :). They are used for spawning and tests
+-export([init_connection_handler/3, % proc_lib:spawn needs this
+ init_ssh_record/3, % Export of this internal function
+ % intended for low-level protocol test suites
+ renegotiate/1, renegotiate_data/1 % Export intended for test cases
+ ]).
%%====================================================================
-%% Internal application API
+%% Start / stop
%%====================================================================
+%%--------------------------------------------------------------------
+-spec start_link(role(),
+ inet:socket(),
+ proplists:proplist()
+ ) -> {ok, pid()}.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+start_link(Role, Socket, Options) ->
+ {ok, proc_lib:spawn_link(?MODULE, init_connection_handler, [Role, Socket, Options])}.
+
%%--------------------------------------------------------------------
--spec start_connection(client| server, port(), proplists:proplist(),
- timeout()) -> {ok, pid()} | {error, term()}.
+-spec stop(connection_ref()
+ ) -> ok | {error, term()}.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+stop(ConnectionHandler)->
+ case call(ConnectionHandler, stop) of
+ {error, closed} ->
+ ok;
+ Other ->
+ Other
+ end.
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+-define(DefaultTransport, {tcp, gen_tcp, tcp_closed} ).
+
%%--------------------------------------------------------------------
+-spec start_connection(role(),
+ inet:socket(),
+ proplists:proplist(),
+ timeout()
+ ) -> {ok, connection_ref()} | {error, term()}.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
start_connection(client = Role, Socket, Options, Timeout) ->
try
{ok, Pid} = sshc_sup:start_child([Role, Socket, Options]),
- {_, Callback, _} =
- proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
- ok = socket_control(Socket, Pid, Callback),
- Ref = erlang:monitor(process, Pid),
- handshake(Pid, Ref, Timeout)
+ ok = socket_control(Socket, Pid, Options),
+ handshake(Pid, erlang:monitor(process,Pid), Timeout)
catch
exit:{noproc, _} ->
{error, ssh_not_started};
@@ -128,8 +124,8 @@ start_connection(server = Role, Socket, Options, Timeout) ->
try
case proplists:get_value(parallel_login, SSH_Opts, false) of
true ->
- HandshakerPid =
- spawn_link(fun() ->
+ HandshakerPid =
+ spawn_link(fun() ->
receive
{do_handshake, Pid} ->
handshake(Pid, erlang:monitor(process,Pid), Timeout)
@@ -148,953 +144,1123 @@ start_connection(server = Role, Socket, Options, Timeout) ->
{error, Error}
end.
-start_the_connection_child(UserPid, Role, Socket, Options) ->
- Sups = proplists:get_value(supervisors, Options),
- ConnectionSup = proplists:get_value(connection_sup, Sups),
- Opts = [{supervisors, Sups}, {user_pid, UserPid} | proplists:get_value(ssh_opts, Options, [])],
- {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),
- Pid.
-
+%%--------------------------------------------------------------------
+%%% Some other module has decided to disconnect.
+-spec disconnect(#ssh_msg_disconnect{}) -> no_return().
+-spec disconnect(#ssh_msg_disconnect{}, iodata()) -> no_return().
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+disconnect(Msg = #ssh_msg_disconnect{}) ->
+ throw({keep_state_and_data,
+ [{next_event, internal, {disconnect, Msg, Msg#ssh_msg_disconnect.description}}]}).
-start_link(Role, Socket, Options) ->
- {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Socket, Options]])}.
+disconnect(Msg = #ssh_msg_disconnect{}, ExtraInfo) ->
+ throw({keep_state_and_data,
+ [{next_event, internal, {disconnect, Msg, {Msg#ssh_msg_disconnect.description,ExtraInfo}}}]}).
-init([Role, Socket, SshOpts]) ->
- process_flag(trap_exit, true),
- {NumVsn, StrVsn} = ssh_transport:versions(Role, SshOpts),
- {Protocol, Callback, CloseTag} =
- proplists:get_value(transport, SshOpts, {tcp, gen_tcp, tcp_closed}),
- Cache = ssh_channel:cache_create(),
- State0 = #state{
- role = Role,
- connection_state = #connection{channel_cache = Cache,
- channel_id_seed = 0,
- port_bindings = [],
- requests = [],
- options = SshOpts},
- socket = Socket,
- decoded_data_buffer = <<>>,
- encoded_data_buffer = <<>>,
- transport_protocol = Protocol,
- transport_cb = Callback,
- transport_close_tag = CloseTag,
- opts = SshOpts
- },
-
- State = init_role(State0),
-
- try init_ssh(Role, NumVsn, StrVsn, SshOpts, Socket) of
- Ssh ->
- gen_fsm:enter_loop(?MODULE, [], hello,
- State#state{ssh_params = Ssh})
- catch
- _:Error ->
- gen_fsm:enter_loop(?MODULE, [], error, {Error, State})
- end.
-%% Temporary fix for the Nessus error. SYN-> <-SYNACK ACK-> RST-> ?
-error(_Event, {Error,State=#state{}}) ->
- case Error of
- {badmatch,{error,enotconn}} ->
- %% {error,enotconn} probably from inet:peername in
- %% init_ssh(server,..)/5 called from init/1
- {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}, State};
- _ ->
- {stop, {shutdown,{init,Error}}, State}
- end;
-error(Event, State) ->
- %% State deliberately not checked beeing #state. This is a panic-clause...
- {stop, {shutdown,{init,{spurious_error,Event}}}, State}.
-
-%%--------------------------------------------------------------------
--spec open_channel(pid(), string(), iodata(), integer(), integer(),
- timeout()) -> {open, channel_id()} | {error, term()}.
%%--------------------------------------------------------------------
-open_channel(ConnectionHandler, ChannelType, ChannelSpecificData,
- InitialWindowSize,
- MaxPacketSize, Timeout) ->
- sync_send_all_state_event(ConnectionHandler, {open, self(), ChannelType,
- InitialWindowSize, MaxPacketSize,
- ChannelSpecificData,
- Timeout}).
-%%--------------------------------------------------------------------
--spec request(pid(), pid(), channel_id(), string(), boolean(), iodata(),
- timeout()) -> success | failure | ok | {error, term()}.
+-spec open_channel(connection_ref(),
+ string(),
+ iodata(),
+ pos_integer(),
+ pos_integer(),
+ timeout()
+ ) -> {open, channel_id()} | {error, term()}.
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+open_channel(ConnectionHandler,
+ ChannelType, ChannelSpecificData, InitialWindowSize, MaxPacketSize,
+ Timeout) ->
+ call(ConnectionHandler,
+ {open,
+ self(),
+ ChannelType, InitialWindowSize, MaxPacketSize, ChannelSpecificData,
+ Timeout}).
+
%%--------------------------------------------------------------------
+-spec request(connection_ref(),
+ pid(),
+ channel_id(),
+ string(),
+ boolean(),
+ iodata(),
+ timeout()
+ ) -> success | failure | ok | {error,timeout}.
+
+-spec request(connection_ref(),
+ channel_id(),
+ string(),
+ boolean(),
+ iodata(),
+ timeout()
+ ) -> success | failure | ok | {error,timeout}.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
request(ConnectionHandler, ChannelPid, ChannelId, Type, true, Data, Timeout) ->
- sync_send_all_state_event(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data,
- Timeout});
+ call(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data, Timeout});
request(ConnectionHandler, ChannelPid, ChannelId, Type, false, Data, _) ->
- send_all_state_event(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data}).
+ cast(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data}).
-%%--------------------------------------------------------------------
--spec request(pid(), channel_id(), string(), boolean(), iodata(),
- timeout()) -> success | failure | {error, timeout}.
-%%--------------------------------------------------------------------
request(ConnectionHandler, ChannelId, Type, true, Data, Timeout) ->
- sync_send_all_state_event(ConnectionHandler, {request, ChannelId, Type, Data, Timeout});
+ call(ConnectionHandler, {request, ChannelId, Type, Data, Timeout});
request(ConnectionHandler, ChannelId, Type, false, Data, _) ->
- send_all_state_event(ConnectionHandler, {request, ChannelId, Type, Data}).
+ cast(ConnectionHandler, {request, ChannelId, Type, Data}).
%%--------------------------------------------------------------------
--spec reply_request(pid(), success | failure, channel_id()) -> ok.
-%%--------------------------------------------------------------------
+-spec reply_request(connection_ref(),
+ success | failure,
+ channel_id()
+ ) -> ok.
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
reply_request(ConnectionHandler, Status, ChannelId) ->
- send_all_state_event(ConnectionHandler, {reply_request, Status, ChannelId}).
-
-%%--------------------------------------------------------------------
--spec global_request(pid(), string(), boolean(), iolist()) -> ok | error.
-%%--------------------------------------------------------------------
-global_request(ConnectionHandler, Type, true = Reply, Data) ->
- case sync_send_all_state_event(ConnectionHandler,
- {global_request, self(), Type, Reply, Data}) of
- {ssh_cm, ConnectionHandler, {success, _}} ->
- ok;
- {ssh_cm, ConnectionHandler, {failure, _}} ->
- error
- end;
-global_request(ConnectionHandler, Type, false = Reply, Data) ->
- send_all_state_event(ConnectionHandler, {global_request, self(), Type, Reply, Data}).
+ cast(ConnectionHandler, {reply_request, Status, ChannelId}).
%%--------------------------------------------------------------------
--spec send(pid(), channel_id(), integer(), iodata(), timeout()) ->
- ok | {error, timeout} | {error, closed}.
-%%--------------------------------------------------------------------
+-spec send(connection_ref(),
+ channel_id(),
+ non_neg_integer(),
+ iodata(),
+ timeout()
+ ) -> ok | {error, timeout|closed}.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
send(ConnectionHandler, ChannelId, Type, Data, Timeout) ->
- sync_send_all_state_event(ConnectionHandler, {data, ChannelId, Type, Data, Timeout}).
+ call(ConnectionHandler, {data, ChannelId, Type, Data, Timeout}).
%%--------------------------------------------------------------------
--spec send_eof(pid(), channel_id()) -> ok | {error, closed}.
-%%--------------------------------------------------------------------
+-spec send_eof(connection_ref(),
+ channel_id()
+ ) -> ok | {error,closed}.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
send_eof(ConnectionHandler, ChannelId) ->
- sync_send_all_state_event(ConnectionHandler, {eof, ChannelId}).
+ call(ConnectionHandler, {eof, ChannelId}).
%%--------------------------------------------------------------------
--spec connection_info(pid(), [atom()]) -> proplists:proplist().
+-spec info(connection_ref()
+ ) -> {ok, [#channel{}]} .
+
+-spec info(connection_ref(),
+ pid() | all
+ ) -> {ok, [#channel{}]} .
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+info(ConnectionHandler) ->
+ info(ConnectionHandler, all).
+
+info(ConnectionHandler, ChannelProcess) ->
+ call(ConnectionHandler, {info, ChannelProcess}).
+
%%--------------------------------------------------------------------
+-type local_sock_info() :: {inet:ip_address(), non_neg_integer()} | string().
+-type peer_sock_info() :: {inet:ip_address(), non_neg_integer()} | string().
+-type state_info() :: iolist().
+
+-spec get_print_info(connection_ref()
+ ) -> {{local_sock_info(), peer_sock_info()},
+ state_info()
+ }.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
get_print_info(ConnectionHandler) ->
- sync_send_all_state_event(ConnectionHandler, get_print_info, 1000).
+ call(ConnectionHandler, get_print_info, 1000).
+%%--------------------------------------------------------------------
+-spec connection_info(connection_ref(),
+ [atom()]
+ ) -> proplists:proplist().
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
connection_info(ConnectionHandler, Options) ->
- sync_send_all_state_event(ConnectionHandler, {connection_info, Options}).
+ call(ConnectionHandler, {connection_info, Options}).
%%--------------------------------------------------------------------
--spec channel_info(pid(), channel_id(), [atom()]) -> proplists:proplist().
-%%--------------------------------------------------------------------
+-spec channel_info(connection_ref(),
+ channel_id(),
+ [atom()]
+ ) -> proplists:proplist().
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
channel_info(ConnectionHandler, ChannelId, Options) ->
- sync_send_all_state_event(ConnectionHandler, {channel_info, ChannelId, Options}).
+ call(ConnectionHandler, {channel_info, ChannelId, Options}).
%%--------------------------------------------------------------------
--spec adjust_window(pid(), channel_id(), integer()) -> ok.
-%%--------------------------------------------------------------------
+-spec adjust_window(connection_ref(),
+ channel_id(),
+ integer()
+ ) -> ok.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
adjust_window(ConnectionHandler, Channel, Bytes) ->
- send_all_state_event(ConnectionHandler, {adjust_window, Channel, Bytes}).
-%%--------------------------------------------------------------------
--spec renegotiate(pid()) -> ok.
-%%--------------------------------------------------------------------
-renegotiate(ConnectionHandler) ->
- send_all_state_event(ConnectionHandler, renegotiate).
-
-%%--------------------------------------------------------------------
--spec renegotiate_data(pid()) -> ok.
-%%--------------------------------------------------------------------
-renegotiate_data(ConnectionHandler) ->
- send_all_state_event(ConnectionHandler, data_size).
+ cast(ConnectionHandler, {adjust_window, Channel, Bytes}).
%%--------------------------------------------------------------------
--spec close(pid(), channel_id()) -> ok.
-%%--------------------------------------------------------------------
+-spec close(connection_ref(),
+ channel_id()
+ ) -> ok.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
close(ConnectionHandler, ChannelId) ->
- case sync_send_all_state_event(ConnectionHandler, {close, ChannelId}) of
+ case call(ConnectionHandler, {close, ChannelId}) of
ok ->
ok;
- {error, closed} ->
+ {error, closed} ->
ok
- end.
-
-%%--------------------------------------------------------------------
--spec stop(pid()) -> ok | {error, term()}.
-%%--------------------------------------------------------------------
-stop(ConnectionHandler)->
- case sync_send_all_state_event(ConnectionHandler, stop) of
- {error, closed} ->
- ok;
- Other ->
- Other
end.
-info(ConnectionHandler) ->
- info(ConnectionHandler, {info, all}).
+%%====================================================================
+%% Test support
+%%====================================================================
+%%--------------------------------------------------------------------
+-spec renegotiate(connection_ref()
+ ) -> ok.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+renegotiate(ConnectionHandler) ->
+ cast(ConnectionHandler, renegotiate).
-info(ConnectionHandler, ChannelProcess) ->
- sync_send_all_state_event(ConnectionHandler, {info, ChannelProcess}).
+%%--------------------------------------------------------------------
+-spec renegotiate_data(connection_ref()
+ ) -> ok.
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+renegotiate_data(ConnectionHandler) ->
+ cast(ConnectionHandler, data_size).
%%====================================================================
-%% gen_fsm callbacks
+%% Internal process state
%%====================================================================
+-record(data, {
+ starter :: pid(),
+ auth_user :: string()
+ | undefined,
+ connection_state :: #connection{},
+ latest_channel_id = 0 :: non_neg_integer(),
+ idle_timer_ref :: undefined
+ | infinity
+ | reference(),
+ idle_timer_value = infinity :: infinity
+ | pos_integer(),
+ transport_protocol :: atom(), % ex: tcp
+ transport_cb :: atom(), % ex: gen_tcp
+ transport_close_tag :: atom(), % ex: tcp_closed
+ ssh_params :: #ssh{}
+ | undefined,
+ socket :: inet:socket(),
+ decrypted_data_buffer = <<>> :: binary(),
+ encrypted_data_buffer = <<>> :: binary(),
+ undecrypted_packet_length :: undefined | non_neg_integer(),
+ key_exchange_init_msg :: #ssh_msg_kexinit{}
+ | undefined,
+ last_size_rekey = 0 :: non_neg_integer(),
+ event_queue = [] :: list(),
+ opts :: proplists:proplist(),
+ inet_initial_recbuf_size :: pos_integer()
+ | undefined
+ }).
+%%====================================================================
+%% Intitialisation
+%%====================================================================
%%--------------------------------------------------------------------
--spec hello(socket_control | {info_line, list()} | {version_exchange, list()},
- #state{}) -> gen_fsm_state_return().
+-spec init_connection_handler(role(),
+ inet:socket(),
+ proplists:proplist()
+ ) -> no_return().
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+init_connection_handler(Role, Socket, Opts) ->
+ process_flag(trap_exit, true),
+ S0 = init_process_state(Role, Socket, Opts),
+ try
+ {Protocol, Callback, CloseTag} =
+ proplists:get_value(transport, Opts, ?DefaultTransport),
+ S0#data{ssh_params = init_ssh_record(Role, Socket, Opts),
+ transport_protocol = Protocol,
+ transport_cb = Callback,
+ transport_close_tag = CloseTag
+ }
+ of
+ S ->
+ gen_statem:enter_loop(?MODULE,
+ [], %%[{debug,[trace,log,statistics,debug]} || Role==server],
+ handle_event_function,
+ {hello,Role},
+ S)
+ catch
+ _:Error ->
+ gen_statem:enter_loop(?MODULE,
+ [],
+ handle_event_function,
+ {init_error,Error},
+ S0)
+ end.
+
+
+init_process_state(Role, Socket, Opts) ->
+ D = #data{connection_state =
+ C = #connection{channel_cache = ssh_channel:cache_create(),
+ channel_id_seed = 0,
+ port_bindings = [],
+ requests = [],
+ options = Opts},
+ starter = proplists:get_value(user_pid, Opts),
+ socket = Socket,
+ opts = Opts
+ },
+ case Role of
+ client ->
+ %% Start the renegotiation timers
+ timer:apply_after(?REKEY_TIMOUT, gen_statem, cast, [self(), renegotiate]),
+ timer:apply_after(?REKEY_DATA_TIMOUT, gen_statem, cast, [self(), data_size]),
+ cache_init_idle_timer(D);
+ server ->
+ D#data{connection_state = init_connection(Role, C, Opts)}
+ end.
+
+
+init_connection(server, C = #connection{}, Opts) ->
+ Sups = proplists:get_value(supervisors, Opts),
+ SystemSup = proplists:get_value(system_sup, Sups),
+ SubSystemSup = proplists:get_value(subsystem_sup, Sups),
+ ConnectionSup = proplists:get_value(connection_sup, Sups),
+ Shell = proplists:get_value(shell, Opts),
+ Exec = proplists:get_value(exec, Opts),
+ CliSpec = proplists:get_value(ssh_cli, Opts, {ssh_cli, [Shell]}),
+ C#connection{cli_spec = CliSpec,
+ exec = Exec,
+ system_supervisor = SystemSup,
+ sub_system_supervisor = SubSystemSup,
+ connection_supervisor = ConnectionSup
+ }.
+
+
+init_ssh_record(Role, Socket, Opts) ->
+ {ok, PeerAddr} = inet:peername(Socket),
+ KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
+ AuthMethods = proplists:get_value(auth_methods, Opts, ?SUPPORTED_AUTH_METHODS),
+ S0 = #ssh{role = Role,
+ key_cb = KeyCb,
+ opts = Opts,
+ userauth_supported_methods = AuthMethods,
+ available_host_keys = supported_host_keys(Role, KeyCb, Opts),
+ random_length_padding = proplists:get_value(max_random_length_padding,
+ Opts,
+ (#ssh{})#ssh.random_length_padding)
+ },
+
+ {Vsn, Version} = ssh_transport:versions(Role, Opts),
+ case Role of
+ client ->
+ PeerName = proplists:get_value(host, Opts),
+ S0#ssh{c_vsn = Vsn,
+ c_version = Version,
+ io_cb = case proplists:get_value(user_interaction, Opts, true) of
+ true -> ssh_io;
+ false -> ssh_no_io
+ end,
+ userauth_quiet_mode = proplists:get_value(quiet_mode, Opts, false),
+ peer = {PeerName, PeerAddr}
+ };
+
+ server ->
+ S0#ssh{s_vsn = Vsn,
+ s_version = Version,
+ io_cb = proplists:get_value(io_cb, Opts, ssh_io),
+ userauth_methods = string:tokens(AuthMethods, ","),
+ kb_tries_left = 3,
+ peer = {undefined, PeerAddr}
+ }
+ end.
+
+
+
+%%====================================================================
+%% gen_statem callbacks
+%%====================================================================
%%--------------------------------------------------------------------
+-type event_content() :: any().
+
+-type renegotiate_flag() :: init | renegotiate.
+
+-type state_name() ::
+ {init_error,any()}
+ | {hello, role()}
+ | {kexinit, role(), renegotiate_flag()}
+ | {key_exchange, role(), renegotiate_flag()}
+ | {key_exchange_dh_gex_init, server, renegotiate_flag()}
+ | {key_exchange_dh_gex_reply, client, renegotiate_flag()}
+ | {new_keys, role()}
+ | {service_request, role()}
+ | {userauth, role()}
+ | {userauth_keyboard_interactive, role()}
+ | {connected, role()}
+ .
+
+-type handle_event_result() :: gen_statem:handle_event_result().
+
+-spec handle_event(gen_statem:event_type(),
+ event_content(),
+ state_name(),
+ #data{}
+ ) -> handle_event_result().
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+%%% ######## Error in the initialisation ####
+
+handle_event(_, _Event, {init_error,Error}, _) ->
+ case Error of
+ {badmatch,{error,enotconn}} ->
+ %% Handles the abnormal sequence:
+ %% SYN->
+ %% <-SYNACK
+ %% ACK->
+ %% RST->
+ {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}};
+
+ OtherError ->
+ {stop, {shutdown,{init,OtherError}}}
+ end;
-hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) ->
- VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)),
- send_msg(VsnMsg, State),
- case getopt(recbuf, Socket) of
- {ok, Size} ->
- inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
- {next_state, hello, State#state{recbuf = Size}};
- {error, Reason} ->
- {stop, {shutdown, Reason}, State}
+
+%%% ######## {hello, client|server} ####
+%% The very first event that is sent when the we are set as controlling process of Socket
+handle_event(_, socket_control, {hello,_}, D) ->
+ VsnMsg = ssh_transport:hello_version_msg(string_version(D#data.ssh_params)),
+ ok = send_bytes(VsnMsg, D),
+ case inet:getopts(Socket=D#data.socket, [recbuf]) of
+ {ok, [{recbuf,Size}]} ->
+ %% Set the socket to the hello text line handling mode:
+ inet:setopts(Socket, [{packet, line},
+ {active, once},
+ % Expecting the version string which might
+ % be max ?MAX_PROTO_VERSION bytes:
+ {recbuf, ?MAX_PROTO_VERSION},
+ {nodelay,true}]),
+ {keep_state, D#data{inet_initial_recbuf_size=Size}};
+
+ Other ->
+ {stop, {shutdown,{unexpected_getopts_return, Other}}}
end;
-hello({info_line, _Line},#state{role = client, socket = Socket} = State) ->
- %% The server may send info lines before the version_exchange
- inet:setopts(Socket, [{active, once}]),
- {next_state, hello, State};
-
-hello({info_line, _Line},#state{role = server,
- socket = Socket,
- transport_cb = Transport } = State) ->
- %% as openssh
- Transport:send(Socket, "Protocol mismatch."),
- {stop, {shutdown,"Protocol mismatch in version exchange."}, State};
-
-hello({version_exchange, Version}, #state{ssh_params = Ssh0,
- socket = Socket,
- recbuf = Size} = State) ->
+handle_event(_, {info_line,_Line}, {hello,Role}, D) ->
+ case Role of
+ client ->
+ %% The server may send info lines to the client before the version_exchange
+ inet:setopts(D#data.socket, [{active, once}]),
+ keep_state_and_data;
+ server ->
+ %% But the client may NOT send them to the server. Openssh answers with cleartext,
+ %% and so do we
+ ok = send_bytes("Protocol mismatch.", D),
+ {stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}}
+ end;
+
+handle_event(_, {version_exchange,Version}, {hello,Role}, D) ->
{NumVsn, StrVsn} = ssh_transport:handle_hello_version(Version),
- case handle_version(NumVsn, StrVsn, Ssh0) of
+ case handle_version(NumVsn, StrVsn, D#data.ssh_params) of
{ok, Ssh1} ->
- inet:setopts(Socket, [{packet,0}, {mode,binary}, {active, once}, {recbuf, Size}]),
+ %% Since the hello part is finnished correctly, we set the
+ %% socket to the packet handling mode (including recbuf size):
+ inet:setopts(D#data.socket, [{packet,0},
+ {mode,binary},
+ {active, once},
+ {recbuf, D#data.inet_initial_recbuf_size}]),
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
- send_msg(SshPacket, State),
- {next_state, kexinit, next_packet(State#state{ssh_params = Ssh,
- key_exchange_init_msg =
- KeyInitMsg})};
+ ok = send_bytes(SshPacket, D),
+ {next_state, {kexinit,Role,init}, D#data{ssh_params = Ssh,
+ key_exchange_init_msg = KeyInitMsg}};
not_supported ->
- DisconnectMsg =
- #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
- description = "Protocol version " ++ StrVsn
- ++ " not supported",
- language = "en"},
- handle_disconnect(DisconnectMsg, State)
- end.
+ disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+ description = ["Protocol version ",StrVsn," not supported"]},
+ {next_state, {hello,Role}, D})
+ end;
-%%--------------------------------------------------------------------
--spec kexinit({#ssh_msg_kexinit{}, binary()}, #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
-kexinit({#ssh_msg_kexinit{} = Kex, Payload},
- #state{ssh_params = #ssh{role = Role} = Ssh0,
- key_exchange_init_msg = OwnKex} =
- State) ->
- Ssh1 = ssh_transport:key_init(opposite_role(Role), Ssh0, Payload),
- case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
- {ok, NextKexMsg, Ssh} when Role == client ->
- send_msg(NextKexMsg, State),
- {next_state, key_exchange,
- next_packet(State#state{ssh_params = Ssh})};
- {ok, Ssh} when Role == server ->
- {next_state, key_exchange,
- next_packet(State#state{ssh_params = Ssh})}
- end.
+
+%%% ######## {kexinit, client|server, init|renegotiate} ####
-%%--------------------------------------------------------------------
--spec key_exchange(#ssh_msg_kexdh_init{} | #ssh_msg_kexdh_reply{} |
- #ssh_msg_kex_dh_gex_group{} | #ssh_msg_kex_dh_gex_request{} |
- #ssh_msg_kex_dh_gex_request{} | #ssh_msg_kex_dh_gex_reply{}, #state{})
- -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
+handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
+ D = #data{key_exchange_init_msg = OwnKex}) ->
+ Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload),
+ Ssh = case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
+ {ok, NextKexMsg, Ssh2} when Role==client ->
+ ok = send_bytes(NextKexMsg, D),
+ Ssh2;
+ {ok, Ssh2} when Role==server ->
+ Ssh2
+ end,
+ {next_state, {key_exchange,Role,ReNeg}, D#data{ssh_params=Ssh}};
-key_exchange(#ssh_msg_kexdh_init{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- case ssh_transport:handle_kexdh_init(Msg, Ssh0) of
- {ok, KexdhReply, Ssh1} ->
- send_msg(KexdhReply, State),
- {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
- end;
-key_exchange(#ssh_msg_kexdh_reply{} = Msg,
- #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- {ok, NewKeys, Ssh} = ssh_transport:handle_kexdh_reply(Msg, Ssh0),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
-
-key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0),
- send_msg(GexGroup, State),
- {next_state, key_exchange_dh_gex_init, next_packet(State#state{ssh_params = Ssh})};
-
-key_exchange(#ssh_msg_kex_dh_gex_request_old{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0),
- send_msg(GexGroup, State),
- {next_state, key_exchange_dh_gex_init, next_packet(State#state{ssh_params = Ssh})};
-
-key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg,
- #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, Ssh0),
- send_msg(KexGexInit, State),
- {next_state, key_exchange_dh_gex_reply, next_packet(State#state{ssh_params = Ssh})};
-
-key_exchange(#ssh_msg_kex_ecdh_init{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, Ssh0),
- send_msg(KexEcdhReply, State),
+%%% ######## {key_exchange, client|server, init|renegotiate} ####
+
+%%%---- diffie-hellman
+handle_event(_, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
+ {ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params),
+ ok = send_bytes(KexdhReply, D),
+ {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
+ ok = send_bytes(NewKeys, D),
+ {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
+
+handle_event(_, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
+ {ok, NewKeys, Ssh} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params),
+ ok = send_bytes(NewKeys, D),
+ {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
+
+%%%---- diffie-hellman group exchange
+handle_event(_, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) ->
+ {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
+ ok = send_bytes(GexGroup, D),
+ {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
+
+handle_event(_, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) ->
+ {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
+ ok = send_bytes(GexGroup, D),
+ {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
+
+handle_event(_, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) ->
+ {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params),
+ ok = send_bytes(KexGexInit, D),
+ {next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}};
+
+%%%---- elliptic curve diffie-hellman
+handle_event(_, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
+ {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params),
+ ok = send_bytes(KexEcdhReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
+ ok = send_bytes(NewKeys, D),
+ {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
-key_exchange(#ssh_msg_kex_ecdh_reply{} = Msg,
- #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- {ok, NewKeys, Ssh} = ssh_transport:handle_kex_ecdh_reply(Msg, Ssh0),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}.
+handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
+ {ok, NewKeys, Ssh} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params),
+ ok = send_bytes(NewKeys, D),
+ {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
-%%--------------------------------------------------------------------
--spec key_exchange_dh_gex_init(#ssh_msg_kex_dh_gex_init{}, #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
-key_exchange_dh_gex_init(#ssh_msg_kex_dh_gex_init{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, KexGexReply, Ssh1} = ssh_transport:handle_kex_dh_gex_init(Msg, Ssh0),
- send_msg(KexGexReply, State),
+
+%%% ######## {key_exchange_dh_gex_init, server, init|renegotiate} ####
+
+handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) ->
+ {ok, KexGexReply, Ssh1} = ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params),
+ ok = send_bytes(KexGexReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}.
+ ok = send_bytes(NewKeys, D),
+ {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
-%%--------------------------------------------------------------------
--spec key_exchange_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{}, #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
-key_exchange_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{} = Msg,
- #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, Ssh0),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh1})}.
-%%--------------------------------------------------------------------
--spec new_keys(#ssh_msg_newkeys{}, #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
+%%% ######## {key_exchange_dh_gex_reply, client, init|renegotiate} ####
-new_keys(#ssh_msg_newkeys{} = Msg, #state{ssh_params = Ssh0} = State0) ->
- {ok, Ssh} = ssh_transport:handle_new_keys(Msg, Ssh0),
- after_new_keys(next_packet(State0#state{ssh_params = Ssh})).
+handle_event(_, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) ->
+ {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params),
+ ok = send_bytes(NewKeys, D),
+ {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh1}};
-%%--------------------------------------------------------------------
--spec service_request(#ssh_msg_service_request{} | #ssh_msg_service_accept{},
- #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
-service_request(#ssh_msg_service_request{name = "ssh-userauth"} = Msg,
- #state{ssh_params = #ssh{role = server,
- session_id = SessionId} = Ssh0} = State) ->
- {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
-
-service_request(#ssh_msg_service_accept{name = "ssh-userauth"},
- #state{ssh_params = #ssh{role = client,
- service = "ssh-userauth"} = Ssh0} =
- State) ->
+
+%%% ######## {new_keys, client|server} ####
+
+%% First key exchange round:
+handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,init}, D) ->
+ {ok, Ssh1} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
+ Ssh = case Role of
+ client ->
+ {MsgReq, Ssh2} = ssh_auth:service_request_msg(Ssh1),
+ ok = send_bytes(MsgReq, D),
+ Ssh2;
+ server ->
+ Ssh1
+ end,
+ {next_state, {service_request,Role}, D#data{ssh_params=Ssh}};
+
+%% Subsequent key exchange rounds (renegotiation):
+handle_event(_, #ssh_msg_newkeys{}, {new_keys,Role,renegotiate}, D) ->
+ {next_state, {connected,Role}, D};
+
+%%% ######## {service_request, client|server}
+
+handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {service_request,server}, D) ->
+ case ServiceName of
+ "ssh-userauth" ->
+ Ssh0 = #ssh{session_id=SessionId} = D#data.ssh_params,
+ {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
+ ok = send_bytes(Reply, D),
+ {next_state, {userauth,server}, D#data{ssh_params = Ssh}};
+
+ _ ->
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "Unknown service"},
+ StateName, D)
+ end;
+
+handle_event(_, #ssh_msg_service_accept{name = "ssh-userauth"}, {service_request,client},
+ #data{ssh_params = #ssh{service="ssh-userauth"} = Ssh0} = State) ->
{Msg, Ssh} = ssh_auth:init_userauth_request_msg(Ssh0),
- send_msg(Msg, State),
- {next_state, userauth, next_packet(State#state{auth_user = Ssh#ssh.user, ssh_params = Ssh})}.
+ ok = send_bytes(Msg, State),
+ {next_state, {userauth,client}, State#data{auth_user = Ssh#ssh.user, ssh_params = Ssh}};
-%%--------------------------------------------------------------------
--spec userauth(#ssh_msg_userauth_request{} | #ssh_msg_userauth_info_request{} |
- #ssh_msg_userauth_info_response{} | #ssh_msg_userauth_success{} |
- #ssh_msg_userauth_failure{} | #ssh_msg_userauth_banner{},
- #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
-userauth(#ssh_msg_userauth_request{service = "ssh-connection",
- method = "none"} = Msg,
- #state{ssh_params = #ssh{session_id = SessionId, role = server,
- service = "ssh-connection"} = Ssh0
- } = State) ->
- {not_authorized, {_User, _Reason}, {Reply, Ssh}} =
- ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
-
-userauth(#ssh_msg_userauth_request{service = "ssh-connection",
- method = Method} = Msg,
- #state{ssh_params = #ssh{session_id = SessionId, role = server,
- service = "ssh-connection",
- peer = {_, Address}} = Ssh0,
- opts = Opts, starter = Pid} = State) ->
- case lists:member(Method, Ssh0#ssh.userauth_methods) of
- true ->
- case ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0) of
- {authorized, User, {Reply, Ssh}} ->
- send_msg(Reply, State),
- Pid ! ssh_connected,
- connected_fun(User, Address, Method, Opts),
- {next_state, connected,
- next_packet(State#state{auth_user = User, ssh_params = Ssh#ssh{authenticated = true}})};
- {not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" ->
- retry_fun(User, Address, Reason, Opts),
- send_msg(Reply, State),
- {next_state, userauth_keyboard_interactive, next_packet(State#state{ssh_params = Ssh})};
- {not_authorized, {User, Reason}, {Reply, Ssh}} ->
- retry_fun(User, Address, Reason, Opts),
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
+
+%%% ######## {userauth, client|server} ####
+
+%%---- userauth request to server
+handle_event(_,
+ Msg = #ssh_msg_userauth_request{service = ServiceName, method = Method},
+ StateName = {userauth,server},
+ D = #data{ssh_params=Ssh0}) ->
+
+ case {ServiceName, Ssh0#ssh.service, Method} of
+ {"ssh-connection", "ssh-connection", "none"} ->
+ %% Probably the very first userauth_request but we deny unauthorized login
+ {not_authorized, _, {Reply,Ssh}} =
+ ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0),
+ ok = send_bytes(Reply, D),
+ {keep_state, D#data{ssh_params = Ssh}};
+
+ {"ssh-connection", "ssh-connection", Method} ->
+ %% Userauth request with a method like "password" or so
+ case lists:member(Method, Ssh0#ssh.userauth_methods) of
+ true ->
+ %% Yepp! we support this method
+ case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of
+ {authorized, User, {Reply, Ssh}} ->
+ ok = send_bytes(Reply, D),
+ D#data.starter ! ssh_connected,
+ connected_fun(User, Method, D),
+ {next_state, {connected,server},
+ D#data{auth_user = User,
+ ssh_params = Ssh#ssh{authenticated = true}}};
+ {not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" ->
+ retry_fun(User, Reason, D),
+ ok = send_bytes(Reply, D),
+ {next_state, {userauth_keyboard_interactive,server}, D#data{ssh_params = Ssh}};
+ {not_authorized, {User, Reason}, {Reply, Ssh}} ->
+ retry_fun(User, Reason, D),
+ ok = send_bytes(Reply, D),
+ {keep_state, D#data{ssh_params = Ssh}}
+ end;
+ false ->
+ %% No we do not support this method (=/= none)
+ %% At least one non-erlang client does like this. Retry as the next event
+ {keep_state_and_data,
+ [{next_event, internal, Msg#ssh_msg_userauth_request{method="none"}}]
+ }
end;
- false ->
- userauth(Msg#ssh_msg_userauth_request{method="none"}, State)
+
+ %% {"ssh-connection", Expected, Method} when Expected =/= ServiceName -> Do what?
+ %% {ServiceName, Expected, Method} when Expected =/= ServiceName -> Do what?
+
+ {ServiceName, _, _} when ServiceName =/= "ssh-connection" ->
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "Unknown service"},
+ StateName, D)
end;
-userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh,
- starter = Pid} = State) ->
- Pid ! ssh_connected,
- {next_state, connected, next_packet(State#state{ssh_params =
- Ssh#ssh{authenticated = true}})};
-userauth(#ssh_msg_userauth_failure{},
- #state{ssh_params = #ssh{role = client,
- userauth_methods = []}}
- = State) ->
- Msg = #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+%%---- userauth success to client
+handle_event(_, #ssh_msg_userauth_success{}, {userauth,client}, D=#data{ssh_params = Ssh}) ->
+ D#data.starter ! ssh_connected,
+ {next_state, {connected,client}, D#data{ssh_params=Ssh#ssh{authenticated = true}}};
+
+
+%%---- userauth failure response to client
+handle_event(_, #ssh_msg_userauth_failure{}, {userauth,client}=StateName,
+ D = #data{ssh_params = #ssh{userauth_methods = []}}) ->
+ Msg = #ssh_msg_disconnect{code = ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
description = "Unable to connect using the available"
- " authentication methods",
- language = "en"},
- handle_disconnect(Msg, State);
-
-%% Server tells us which authentication methods that are allowed
-userauth(#ssh_msg_userauth_failure{authentications = Methodes},
- #state{ssh_params = #ssh{role = client,
- userauth_methods = none} = Ssh0} = State) ->
- AuthMethods = string:tokens(Methodes, ","),
- Ssh1 = Ssh0#ssh{userauth_methods = AuthMethods},
+ " authentication methods"},
+ disconnect(Msg, StateName, D);
+
+handle_event(_, #ssh_msg_userauth_failure{authentications = Methods}, StateName={userauth,client},
+ D = #data{ssh_params = Ssh0}) ->
+ %% The prefered authentication method failed try next method
+ Ssh1 = case Ssh0#ssh.userauth_methods of
+ none ->
+ %% Server tells us which authentication methods that are allowed
+ Ssh0#ssh{userauth_methods = string:tokens(Methods, ",")};
+ _ ->
+ %% We already know...
+ Ssh0
+ end,
case ssh_auth:userauth_request_msg(Ssh1) of
{disconnect, DisconnectMsg, {Msg, Ssh}} ->
- send_msg(Msg, State),
- handle_disconnect(DisconnectMsg, State#state{ssh_params = Ssh});
+ send_bytes(Msg, D),
+ disconnect(DisconnectMsg, StateName, D#data{ssh_params = Ssh});
{"keyboard-interactive", {Msg, Ssh}} ->
- send_msg(Msg, State),
- {next_state, userauth_keyboard_interactive, next_packet(State#state{ssh_params = Ssh})};
+ send_bytes(Msg, D),
+ {next_state, {userauth_keyboard_interactive,client}, D#data{ssh_params = Ssh}};
{_Method, {Msg, Ssh}} ->
- send_msg(Msg, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
+ send_bytes(Msg, D),
+ {keep_state, D#data{ssh_params = Ssh}}
end;
-%% The prefered authentication method failed try next method
-userauth(#ssh_msg_userauth_failure{},
- #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- case ssh_auth:userauth_request_msg(Ssh0) of
- {disconnect, DisconnectMsg,{Msg, Ssh}} ->
- send_msg(Msg, State),
- handle_disconnect(DisconnectMsg, State#state{ssh_params = Ssh});
- {"keyboard-interactive", {Msg, Ssh}} ->
- send_msg(Msg, State),
- {next_state, userauth_keyboard_interactive, next_packet(State#state{ssh_params = Ssh})};
- {_Method, {Msg, Ssh}} ->
- send_msg(Msg, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
- end;
+%%---- banner to client
+handle_event(_, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) ->
+ case D#data.ssh_params#ssh.userauth_quiet_mode of
+ false -> io:format("~s", [Msg]);
+ true -> ok
+ end,
+ keep_state_and_data;
-userauth(#ssh_msg_userauth_banner{},
- #state{ssh_params = #ssh{userauth_quiet_mode = true,
- role = client}} = State) ->
- {next_state, userauth, next_packet(State)};
-userauth(#ssh_msg_userauth_banner{message = Msg},
- #state{ssh_params =
- #ssh{userauth_quiet_mode = false, role = client}} = State) ->
- io:format("~s", [Msg]),
- {next_state, userauth, next_packet(State)}.
-
-
-
-userauth_keyboard_interactive(#ssh_msg_userauth_info_request{} = Msg,
- #state{ssh_params = #ssh{role = client,
- io_cb = IoCb} = Ssh0} = State) ->
- {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_request(Msg, IoCb, Ssh0),
- send_msg(Reply, State),
- {next_state, userauth_keyboard_interactive_info_response, next_packet(State#state{ssh_params = Ssh})};
-
-userauth_keyboard_interactive(#ssh_msg_userauth_info_response{} = Msg,
- #state{ssh_params = #ssh{role = server,
- peer = {_, Address}} = Ssh0,
- opts = Opts, starter = Pid} = State) ->
- case ssh_auth:handle_userauth_info_response(Msg, Ssh0) of
+
+%%% ######## {userauth_keyboard_interactive, client|server}
+
+handle_event(_, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client},
+ #data{ssh_params = Ssh0} = D) ->
+ {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_request(Msg, Ssh0#ssh.io_cb, Ssh0),
+ send_bytes(Reply, D),
+ {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}};
+
+handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D) ->
+ case ssh_auth:handle_userauth_info_response(Msg, D#data.ssh_params) of
{authorized, User, {Reply, Ssh}} ->
- send_msg(Reply, State),
- Pid ! ssh_connected,
- connected_fun(User, Address, "keyboard-interactive", Opts),
- {next_state, connected,
- next_packet(State#state{auth_user = User, ssh_params = Ssh#ssh{authenticated = true}})};
+ send_bytes(Reply, D),
+ D#data.starter ! ssh_connected,
+ connected_fun(User, "keyboard-interactive", D),
+ {next_state, {connected,server}, D#data{auth_user = User,
+ ssh_params = Ssh#ssh{authenticated = true}}};
{not_authorized, {User, Reason}, {Reply, Ssh}} ->
- retry_fun(User, Address, Reason, Opts),
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
+ retry_fun(User, Reason, D),
+ send_bytes(Reply, D),
+ {next_state, {userauth,server}, D#data{ssh_params = Ssh}}
end;
-userauth_keyboard_interactive(Msg = #ssh_msg_userauth_failure{},
- #state{ssh_params = Ssh0 =
- #ssh{role = client,
- userauth_preference = Prefs0}}
- = State) ->
- Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Prefs0,
+
+handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client},
+ #data{ssh_params = Ssh0} = D0) ->
+ Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference,
Method =/= "keyboard-interactive"],
- userauth(Msg, State#state{ssh_params = Ssh0#ssh{userauth_preference=Prefs}}).
+ D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}},
+ {next_state, {userauth,client}, D, [{next_event, internal, Msg}]};
+handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client}, D) ->
+ {next_state, {userauth,client}, D, [{next_event, internal, Msg}]};
+handle_event(_, Msg=#ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) ->
+ {next_state, {userauth,client}, D, [{next_event, internal, Msg}]};
-userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_failure{},
- #state{ssh_params = #ssh{role = client}} = State) ->
- userauth(Msg, State);
-userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_success{},
- #state{ssh_params = #ssh{role = client}} = State) ->
- userauth(Msg, State);
-userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_info_request{},
- #state{ssh_params = #ssh{role = client}} = State) ->
- userauth_keyboard_interactive(Msg, State).
+handle_event(_, Msg=#ssh_msg_userauth_info_request{}, {userauth_keyboard_interactive_info_response, client}, D) ->
+ {next_state, {userauth_keyboard_interactive,client}, D, [{next_event, internal, Msg}]};
-%%--------------------------------------------------------------------
--spec connected({#ssh_msg_kexinit{}, binary()}, %%| %% #ssh_msg_kexdh_init{},
- #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
-connected({#ssh_msg_kexinit{}, _Payload} = Event, #state{ssh_params = Ssh0} = State0) ->
- {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0),
- State = State0#state{ssh_params = Ssh,
- key_exchange_init_msg = KeyInitMsg,
- renegotiate = true},
- send_msg(SshPacket, State),
- kexinit(Event, State).
-%%--------------------------------------------------------------------
--spec handle_event(#ssh_msg_disconnect{} | #ssh_msg_ignore{} | #ssh_msg_debug{} |
- #ssh_msg_unimplemented{} | {adjust_window, integer(), integer()} |
- {reply_request, success | failure, integer()} | renegotiate |
- data_size | {request, pid(), integer(), integer(), iolist()} |
- {request, integer(), integer(), iolist()}, state_name(),
- #state{}) -> gen_fsm_state_return().
+%%% ######## {connected, client|server} ####
-%%--------------------------------------------------------------------
-handle_event(#ssh_msg_disconnect{description = Desc} = DisconnectMsg, _StateName, #state{} = State) ->
- handle_disconnect(peer, DisconnectMsg, State),
- {stop, {shutdown, Desc}, State};
-
-handle_event(#ssh_msg_ignore{}, StateName, State) ->
- {next_state, StateName, next_packet(State)};
-
-handle_event(#ssh_msg_debug{always_display = Display, message = DbgMsg, language=Lang},
- StateName, #state{opts = Opts} = State) ->
- F = proplists:get_value(ssh_msg_debug_fun, Opts,
- fun(_ConnRef, _AlwaysDisplay, _Msg, _Language) -> ok end
- ),
- catch F(self(), Display, DbgMsg, Lang),
- {next_state, StateName, next_packet(State)};
-
-handle_event(#ssh_msg_unimplemented{}, StateName, State) ->
- {next_state, StateName, next_packet(State)};
-
-handle_event(renegotiate, connected, #state{ssh_params = Ssh0}
- = State) ->
- {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0),
- send_msg(SshPacket, State),
- timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]),
- {next_state, kexinit,
- next_packet(State#state{ssh_params = Ssh,
- key_exchange_init_msg = KeyInitMsg,
- renegotiate = true})};
-
-handle_event(renegotiate, StateName, State) ->
+handle_event(_, {#ssh_msg_kexinit{},_} = Event, {connected,Role}, D0) ->
+ {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(D0#data.ssh_params),
+ D = D0#data{ssh_params = Ssh,
+ key_exchange_init_msg = KeyInitMsg},
+ send_bytes(SshPacket, D),
+ {next_state, {kexinit,Role,renegotiate}, D, [{next_event, internal, Event}]};
+
+handle_event(_, #ssh_msg_disconnect{description=Desc} = Msg, StateName, D0) ->
+ {disconnect, _, {{replies,Replies}, _}} =
+ ssh_connection:handle_msg(Msg, D0#data.connection_state, role(StateName)),
+ {Actions,D} = send_replies(Replies, D0),
+ disconnect_fun(Desc, D),
+ {stop_and_reply, {shutdown,Desc}, Actions, D};
+
+handle_event(_, #ssh_msg_ignore{}, _, _) ->
+ keep_state_and_data;
+
+handle_event(_, #ssh_msg_unimplemented{}, _, _) ->
+ keep_state_and_data;
+
+handle_event(_, #ssh_msg_debug{} = Msg, _, D) ->
+ debug_fun(Msg, D),
+ keep_state_and_data;
+
+handle_event(internal, Msg=#ssh_msg_global_request{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_request_success{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_request_failure{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_open{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_open_confirmation{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_open_failure{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_window_adjust{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_data{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_extended_data{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_eof{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_close{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_request{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_success{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+handle_event(internal, Msg=#ssh_msg_channel_failure{}, StateName, D) ->
+ handle_connection_msg(Msg, StateName, D);
+
+
+handle_event(cast, renegotiate, {connected,Role}, D) ->
+ {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(D#data.ssh_params),
+ send_bytes(SshPacket, D),
+ timer:apply_after(?REKEY_TIMOUT, gen_statem, cast, [self(), renegotiate]),
+ {next_state, {kexinit,Role,renegotiate}, D#data{ssh_params = Ssh,
+ key_exchange_init_msg = KeyInitMsg}};
+
+handle_event(cast, renegotiate, _, _) ->
%% Already in key-exchange so safe to ignore
- {next_state, StateName, State};
+ timer:apply_after(?REKEY_TIMOUT, gen_statem, cast, [self(), renegotiate]), % FIXME: not here in original
+ keep_state_and_data;
+
%% Rekey due to sent data limit reached?
-handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) ->
- {ok, [{send_oct,Sent0}]} = inet:getstat(State#state.socket, [send_oct]),
- Sent = Sent0 - State#state.last_size_rekey,
- MaxSent = proplists:get_value(rekey_limit, State#state.opts, 1024000000),
- timer:apply_after(?REKEY_DATA_TIMOUT, gen_fsm, send_all_state_event, [self(), data_size]),
+handle_event(cast, data_size, {connected,Role}, D) ->
+ {ok, [{send_oct,Sent0}]} = inet:getstat(D#data.socket, [send_oct]),
+ Sent = Sent0 - D#data.last_size_rekey,
+ MaxSent = proplists:get_value(rekey_limit, D#data.opts, 1024000000),
+ timer:apply_after(?REKEY_DATA_TIMOUT, gen_statem, cast, [self(), data_size]),
case Sent >= MaxSent of
true ->
- {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0),
- send_msg(SshPacket, State),
- {next_state, kexinit,
- next_packet(State#state{ssh_params = Ssh,
- key_exchange_init_msg = KeyInitMsg,
- renegotiate = true,
- last_size_rekey = Sent0})};
+ {KeyInitMsg, SshPacket, Ssh} =
+ ssh_transport:key_exchange_init_msg(D#data.ssh_params),
+ send_bytes(SshPacket, D),
+ {next_state, {kexinit,Role,renegotiate}, D#data{ssh_params = Ssh,
+ key_exchange_init_msg = KeyInitMsg,
+ last_size_rekey = Sent0}};
_ ->
- {next_state, connected, next_packet(State)}
+ keep_state_and_data
end;
-handle_event(data_size, StateName, State) ->
+
+handle_event(cast, data_size, _, _) ->
%% Already in key-exchange so safe to ignore
- {next_state, StateName, State};
-
-handle_event(Event, StateName, State) when StateName /= connected ->
- Events = [{event, Event} | State#state.event_queue],
- {next_state, StateName, State#state{event_queue = Events}};
-
-handle_event({adjust_window, ChannelId, Bytes}, StateName,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- State =
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{recv_window_size = WinSize,
- recv_window_pending = Pending,
- recv_packet_size = PktSize} = Channel
- when (WinSize-Bytes) >= 2*PktSize ->
- %% The peer can send at least two more *full* packet, no hurry.
- ssh_channel:cache_update(Cache,
- Channel#channel{recv_window_pending = Pending + Bytes}),
- State0;
-
- #channel{recv_window_size = WinSize,
- recv_window_pending = Pending,
- remote_id = Id} = Channel ->
- %% Now we have to update the window - we can't receive so many more pkts
- ssh_channel:cache_update(Cache,
- Channel#channel{recv_window_size =
- WinSize + Bytes + Pending,
- recv_window_pending = 0}),
- Msg = ssh_connection:channel_adjust_window_msg(Id, Bytes + Pending),
- send_replies([{connection_reply, Msg}], State0);
+ timer:apply_after(?REKEY_DATA_TIMOUT, gen_statem, cast, [self(), data_size]), % FIXME: not here in original
+ keep_state_and_data;
+
+
+
+handle_event(cast, _, StateName, _) when StateName /= {connected,server},
+ StateName /= {connected,client} ->
+ {keep_state_and_data, [postpone]};
+
+
+handle_event(cast, {adjust_window,ChannelId,Bytes}, {connected,_}, D) ->
+ case ssh_channel:cache_lookup(cache(D), ChannelId) of
+ #channel{recv_window_size = WinSize,
+ recv_window_pending = Pending,
+ recv_packet_size = PktSize} = Channel
+ when (WinSize-Bytes) >= 2*PktSize ->
+ %% The peer can send at least two more *full* packet, no hurry.
+ ssh_channel:cache_update(cache(D),
+ Channel#channel{recv_window_pending = Pending + Bytes}),
+ keep_state_and_data;
+
+ #channel{recv_window_size = WinSize,
+ recv_window_pending = Pending,
+ remote_id = Id} = Channel ->
+ %% Now we have to update the window - we can't receive so many more pkts
+ ssh_channel:cache_update(cache(D),
+ Channel#channel{recv_window_size =
+ WinSize + Bytes + Pending,
+ recv_window_pending = 0}),
+ Msg = ssh_connection:channel_adjust_window_msg(Id, Bytes + Pending),
+ {keep_state, send_msg(Msg,D)};
- undefined ->
- State0
- end,
- {next_state, StateName, next_packet(State)};
-
-handle_event({reply_request, success, ChannelId}, StateName,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- State = case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = RemoteId} ->
- Msg = ssh_connection:channel_success_msg(RemoteId),
- send_replies([{connection_reply, Msg}], State0);
- undefined ->
- State0
- end,
- {next_state, StateName, State};
-
-handle_event({request, ChannelPid, ChannelId, Type, Data}, StateName, State0) ->
- {{replies, Replies}, State1} = handle_request(ChannelPid, ChannelId,
- Type, Data,
- false, none, State0),
- State = send_replies(Replies, State1),
- {next_state, StateName, next_packet(State)};
-
-handle_event({request, ChannelId, Type, Data}, StateName, State0) ->
- {{replies, Replies}, State1} = handle_request(ChannelId, Type, Data,
- false, none, State0),
- State = send_replies(Replies, State1),
- {next_state, StateName, next_packet(State)};
-
-handle_event({unknown, Data}, StateName, State) ->
+ undefined ->
+ keep_state_and_data
+ end;
+
+handle_event(cast, {reply_request,success,ChannelId}, {connected,_}, D) ->
+ case ssh_channel:cache_lookup(cache(D), ChannelId) of
+ #channel{remote_id = RemoteId} ->
+ Msg = ssh_connection:channel_success_msg(RemoteId),
+ {keep_state, send_msg(Msg,D)};
+
+ undefined ->
+ keep_state_and_data
+ end;
+
+handle_event(cast, {request,ChannelPid, ChannelId, Type, Data}, {connected,_}, D) ->
+ {keep_state, handle_request(ChannelPid, ChannelId, Type, Data, false, none, D)};
+
+handle_event(cast, {request,ChannelId,Type,Data}, {connected,_}, D) ->
+ {keep_state, handle_request(ChannelId, Type, Data, false, none, D)};
+
+handle_event(cast, {unknown,Data}, {connected,_}, D) ->
Msg = #ssh_msg_unimplemented{sequence = Data},
- send_msg(Msg, State),
- {next_state, StateName, next_packet(State)}.
+ {keep_state, send_msg(Msg,D)};
-%%--------------------------------------------------------------------
--spec handle_sync_event({request, pid(), channel_id(), integer(), binary(), timeout()} |
- {request, channel_id(), integer(), binary(), timeout()} |
- {global_request, pid(), integer(), boolean(), binary()} | {eof, integer()} |
- {open, pid(), integer(), channel_id(), integer(), binary(), _} |
- {send_window, channel_id()} | {recv_window, channel_id()} |
- {connection_info, [client_version | server_version | peer |
- sockname]} | {channel_info, channel_id(), [recv_window |
- send_window]} |
- {close, channel_id()} | stop, term(), state_name(), #state{})
- -> gen_fsm_sync_return().
-%%--------------------------------------------------------------------
-handle_sync_event(get_print_info, _From, StateName, State) ->
+%%% Previously handle_sync_event began here
+handle_event({call,From}, get_print_info, StateName, D) ->
Reply =
try
- {inet:sockname(State#state.socket),
- inet:peername(State#state.socket)
+ {inet:sockname(D#data.socket),
+ inet:peername(D#data.socket)
}
of
- {{ok,Local}, {ok,Remote}} -> {{Local,Remote},io_lib:format("statename=~p",[StateName])};
- _ -> {{"-",0},"-"}
+ {{ok,Local}, {ok,Remote}} ->
+ {{Local,Remote},io_lib:format("statename=~p",[StateName])};
+ _ ->
+ {{"-",0},"-"}
catch
- _:_ -> {{"?",0},"?"}
+ _:_ ->
+ {{"?",0},"?"}
end,
- {reply, Reply, StateName, State};
+ {keep_state_and_data, [{reply,From,Reply}]};
-handle_sync_event({connection_info, Options}, _From, StateName, State) ->
- Info = ssh_info(Options, State, []),
- {reply, Info, StateName, State};
+handle_event({call,From}, {connection_info, Options}, _, D) ->
+ Info = ssh_info(Options, D, []),
+ {keep_state_and_data, [{reply,From,Info}]};
-handle_sync_event({channel_info, ChannelId, Options}, _From, StateName,
- #state{connection_state = #connection{channel_cache = Cache}} = State) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{} = Channel ->
+handle_event({call,From}, {channel_info,ChannelId,Options}, _, D) ->
+ case ssh_channel:cache_lookup(cache(D), ChannelId) of
+ #channel{} = Channel ->
Info = ssh_channel_info(Options, Channel, []),
- {reply, Info, StateName, State};
+ {keep_state_and_data, [{reply,From,Info}]};
undefined ->
- {reply, [], StateName, State}
+ {keep_state_and_data, [{reply,From,[]}]}
end;
-handle_sync_event({info, ChannelPid}, _From, StateName,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State) ->
+
+handle_event({call,From}, {info, all}, _, D) ->
+ Result = ssh_channel:cache_foldl(fun(Channel, Acc) ->
+ [Channel | Acc]
+ end,
+ [], cache(D)),
+ {keep_state_and_data, [{reply, From, {ok,Result}}]};
+
+handle_event({call,From}, {info, ChannelPid}, _, D) ->
Result = ssh_channel:cache_foldl(
- fun(Channel, Acc) when ChannelPid == all;
- Channel#channel.user == ChannelPid ->
+ fun(Channel, Acc) when Channel#channel.user == ChannelPid ->
[Channel | Acc];
(_, Acc) ->
Acc
- end, [], Cache),
- {reply, {ok, Result}, StateName, State};
+ end, [], cache(D)),
+ {keep_state_and_data, [{reply, From, {ok,Result}}]};
-handle_sync_event(stop, _, _StateName, #state{connection_state = Connection0,
- role = Role} = State0) ->
+handle_event({call,From}, stop, StateName, D0) ->
{disconnect, _Reason, {{replies, Replies}, Connection}} =
ssh_connection:handle_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "User closed down connection",
- language = "en"}, Connection0, Role),
- State = send_replies(Replies, State0),
- {stop, normal, ok, State#state{connection_state = Connection}};
-
-
-handle_sync_event(Event, From, StateName, State) when StateName /= connected ->
- Events = [{sync, Event, From} | State#state.event_queue],
- {next_state, StateName, State#state{event_queue = Events}};
-
-handle_sync_event({request, ChannelPid, ChannelId, Type, Data, Timeout}, From, StateName, State0) ->
- {{replies, Replies}, State1} = handle_request(ChannelPid,
- ChannelId, Type, Data,
- true, From, State0),
- %% Note reply to channel will happen later when
- %% reply is recived from peer on the socket
- State = send_replies(Replies, State1),
- start_timeout(ChannelId, From, Timeout),
- handle_idle_timeout(State),
- {next_state, StateName, next_packet(State)};
-
-handle_sync_event({request, ChannelId, Type, Data, Timeout}, From, StateName, State0) ->
- {{replies, Replies}, State1} = handle_request(ChannelId, Type, Data,
- true, From, State0),
- %% Note reply to channel will happen later when
- %% reply is recived from peer on the socket
- State = send_replies(Replies, State1),
- start_timeout(ChannelId, From, Timeout),
- handle_idle_timeout(State),
- {next_state, StateName, next_packet(State)};
-
-handle_sync_event({global_request, Pid, _, _, _} = Request, From, StateName,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- State1 = handle_global_request(Request, State0),
- Channel = ssh_channel:cache_find(Pid, Cache),
- State = add_request(true, Channel#channel.local_id, From, State1),
- {next_state, StateName, next_packet(State)};
-
-handle_sync_event({data, ChannelId, Type, Data, Timeout}, From, StateName,
- #state{connection_state = #connection{channel_cache = _Cache}
- = Connection0} = State0) ->
-
- case ssh_connection:channel_data(ChannelId, Type, Data, Connection0, From) of
- {{replies, Replies}, Connection} ->
- State = send_replies(Replies, State0#state{connection_state = Connection}),
- start_timeout(ChannelId, From, Timeout),
- {next_state, StateName, next_packet(State)};
- {noreply, Connection} ->
- start_timeout(ChannelId, From, Timeout),
- {next_state, StateName, next_packet(State0#state{connection_state = Connection})}
- end;
-
-handle_sync_event({eof, ChannelId}, _From, StateName,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
+ description = "User closed down connection"},
+ D0#data.connection_state,
+ role(StateName)),
+ {Repls,D} = send_replies(Replies, D0),
+ {stop_and_reply, normal, [{reply,From,ok}|Repls], D#data{connection_state=Connection}};
+
+handle_event({call,_}, _, StateName, _) when StateName /= {connected,server},
+ StateName /= {connected,client} ->
+ {keep_state_and_data, [postpone]};
+
+handle_event({call,From}, {request, ChannelPid, ChannelId, Type, Data, Timeout}, {connected,_}, D0) ->
+ D = handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0),
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)};
+
+handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, {connected,_}, D0) ->
+ D = handle_request(ChannelId, Type, Data, true, From, D0),
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)};
+
+handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, {connected,_}, D0) ->
+ {{replies, Replies}, Connection} =
+ ssh_connection:channel_data(ChannelId, Type, Data, D0#data.connection_state, From),
+ {Repls,D} = send_replies(Replies, D0#data{connection_state = Connection}),
+ start_channel_request_timer(ChannelId, From, Timeout), % FIXME: No message exchange so why?
+ {keep_state, D, Repls};
+
+handle_event({call,From}, {eof, ChannelId}, {connected,_}, D0) ->
+ case ssh_channel:cache_lookup(cache(D0), ChannelId) of
#channel{remote_id = Id, sent_close = false} ->
- State = send_replies([{connection_reply,
- ssh_connection:channel_eof_msg(Id)}], State0),
- {reply, ok, StateName, next_packet(State)};
+ D = send_msg(ssh_connection:channel_eof_msg(Id), D0),
+ {keep_state, D, [{reply,From,ok}]};
_ ->
- {reply, {error,closed}, StateName, State0}
+ {keep_state, D0, [{reply,From,{error,closed}}]}
end;
-handle_sync_event({open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data, Timeout},
- From, StateName, #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
+handle_event({call,From},
+ {open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data, Timeout},
+ {connected,_},
+ D0) ->
erlang:monitor(process, ChannelPid),
- {ChannelId, State1} = new_channel_id(State0),
- Msg = ssh_connection:channel_open_msg(Type, ChannelId,
- InitialWindowSize,
- MaxPacketSize, Data),
- State2 = send_replies([{connection_reply, Msg}], State1),
- Channel = #channel{type = Type,
- sys = "none",
- user = ChannelPid,
- local_id = ChannelId,
- recv_window_size = InitialWindowSize,
- recv_packet_size = MaxPacketSize,
- send_buf = queue:new()
- },
- ssh_channel:cache_update(Cache, Channel),
- State = add_request(true, ChannelId, From, State2),
- start_timeout(ChannelId, From, Timeout),
- {next_state, StateName, next_packet(remove_timer_ref(State))};
-
-handle_sync_event({send_window, ChannelId}, _From, StateName,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State) ->
- Reply = case ssh_channel:cache_lookup(Cache, ChannelId) of
+ {ChannelId, D1} = new_channel_id(D0),
+ D2 = send_msg(ssh_connection:channel_open_msg(Type, ChannelId,
+ InitialWindowSize,
+ MaxPacketSize, Data),
+ D1),
+ ssh_channel:cache_update(cache(D2),
+ #channel{type = Type,
+ sys = "none",
+ user = ChannelPid,
+ local_id = ChannelId,
+ recv_window_size = InitialWindowSize,
+ recv_packet_size = MaxPacketSize,
+ send_buf = queue:new()
+ }),
+ D = add_request(true, ChannelId, From, D2),
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_cancel_idle_timer(D)};
+
+handle_event({call,From}, {send_window, ChannelId}, {connected,_}, D) ->
+ Reply = case ssh_channel:cache_lookup(cache(D), ChannelId) of
#channel{send_window_size = WinSize,
send_packet_size = Packsize} ->
{ok, {WinSize, Packsize}};
undefined ->
{error, einval}
end,
- {reply, Reply, StateName, next_packet(State)};
-
-handle_sync_event({recv_window, ChannelId}, _From, StateName,
- #state{connection_state = #connection{channel_cache = Cache}}
- = State) ->
+ {keep_state_and_data, [{reply,From,Reply}]};
- Reply = case ssh_channel:cache_lookup(Cache, ChannelId) of
+handle_event({call,From}, {recv_window, ChannelId}, {connected,_}, D) ->
+ Reply = case ssh_channel:cache_lookup(cache(D), ChannelId) of
#channel{recv_window_size = WinSize,
recv_packet_size = Packsize} ->
{ok, {WinSize, Packsize}};
undefined ->
{error, einval}
end,
- {reply, Reply, StateName, next_packet(State)};
-
-handle_sync_event({close, ChannelId}, _, StateName,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- State =
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id} = Channel ->
- State1 = send_replies([{connection_reply,
- ssh_connection:channel_close_msg(Id)}], State0),
- ssh_channel:cache_update(Cache, Channel#channel{sent_close = true}),
- handle_idle_timeout(State1),
- State1;
- undefined ->
- State0
- end,
- {reply, ok, StateName, next_packet(State)}.
+ {keep_state_and_data, [{reply,From,Reply}]};
+
+handle_event({call,From}, {close, ChannelId}, {connected,_}, D0) ->
+ case ssh_channel:cache_lookup(cache(D0), ChannelId) of
+ #channel{remote_id = Id} = Channel ->
+ D1 = send_msg(ssh_connection:channel_close_msg(Id), D0),
+ ssh_channel:cache_update(cache(D1), Channel#channel{sent_close = true}),
+ {keep_state, cache_request_idle_timer_check(D1), [{reply,From,ok}]};
+ undefined ->
+ {keep_state_and_data, [{reply,From,ok}]}
+ end;
-%%--------------------------------------------------------------------
--spec handle_info({atom(), port(), binary()} | {atom(), port()} |
- term (), state_name(), #state{}) -> gen_fsm_state_return().
-%%--------------------------------------------------------------------
-handle_info({Protocol, Socket, "SSH-" ++ _ = Version}, hello,
- #state{socket = Socket,
- transport_protocol = Protocol} = State ) ->
- event({version_exchange, Version}, hello, State);
-
-handle_info({Protocol, Socket, Info}, hello,
- #state{socket = Socket,
- transport_protocol = Protocol} = State) ->
- event({info_line, Info}, hello, State);
-
-handle_info({Protocol, Socket, Data}, StateName,
- #state{socket = Socket,
- transport_protocol = Protocol,
- ssh_params = Ssh0,
- decoded_data_buffer = DecData0,
- encoded_data_buffer = EncData0,
- undecoded_packet_length = RemainingSshPacketLen0} = State0) ->
- Encoded = <<EncData0/binary, Data/binary>>,
- try ssh_transport:handle_packet_part(DecData0, Encoded, RemainingSshPacketLen0, Ssh0)
+%%===== Reception of encrypted bytes, decryption and framing
+handle_event(info, {Proto, Sock, Info}, {hello,_}, #data{socket = Sock,
+ transport_protocol = Proto}) ->
+ case Info of
+ "SSH-" ++ _ ->
+ {keep_state_and_data, [{next_event, internal, {version_exchange,Info}}]};
+ _ ->
+ {keep_state_and_data, [{next_event, internal, {info_line,Info}}]}
+ end;
+
+handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock,
+ transport_protocol = Proto}) ->
+ try ssh_transport:handle_packet_part(
+ D0#data.decrypted_data_buffer,
+ <<(D0#data.encrypted_data_buffer)/binary, NewData/binary>>,
+ D0#data.undecrypted_packet_length,
+ D0#data.ssh_params)
of
- {get_more, DecBytes, EncDataRest, RemainingSshPacketLen, Ssh1} ->
- {next_state, StateName,
- next_packet(State0#state{encoded_data_buffer = EncDataRest,
- decoded_data_buffer = DecBytes,
- undecoded_packet_length = RemainingSshPacketLen,
- ssh_params = Ssh1})};
- {decoded, MsgBytes, EncDataRest, Ssh1} ->
- generate_event(MsgBytes, StateName,
- State0#state{ssh_params = Ssh1,
- %% Important to be set for
- %% next_packet
-%%% FIXME: the following three seem to always be set in generate_event!
- decoded_data_buffer = <<>>,
- undecoded_packet_length = undefined,
- encoded_data_buffer = EncDataRest},
- EncDataRest);
+ {packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} ->
+ D = D0#data{ssh_params =
+ Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)},
+ decrypted_data_buffer = <<>>,
+ undecrypted_packet_length = undefined,
+ encrypted_data_buffer = EncryptedDataRest},
+ try
+ ssh_message:decode(set_kex_overload_prefix(DecryptedBytes,D))
+ of
+ Msg = #ssh_msg_kexinit{} ->
+ {keep_state, D, [{next_event, internal, {Msg,DecryptedBytes}},
+ {next_event, internal, prepare_next_packet}
+ ]};
+ Msg ->
+ {keep_state, D, [{next_event, internal, Msg},
+ {next_event, internal, prepare_next_packet}
+ ]}
+ catch
+ _C:_E ->
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Encountered unexpected input"},
+ StateName, D)
+ end;
+
+ {get_more, DecryptedBytes, EncryptedDataRest, RemainingSshPacketLen, Ssh1} ->
+ %% Here we know that there are not enough bytes in
+ %% EncryptedDataRest to use. We must wait for more.
+ inet:setopts(Sock, [{active, once}]),
+ {keep_state, D0#data{encrypted_data_buffer = EncryptedDataRest,
+ decrypted_data_buffer = DecryptedBytes,
+ undecrypted_packet_length = RemainingSshPacketLen,
+ ssh_params = Ssh1}};
+
{bad_mac, Ssh1} ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad mac",
- language = ""},
- handle_disconnect(DisconnectMsg, State0#state{ssh_params=Ssh1});
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad mac"},
+ StateName, D0#data{ssh_params=Ssh1});
{error, {exceeds_max_size,PacketLen}} ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad packet length "
- ++ integer_to_list(PacketLen),
- language = ""},
- handle_disconnect(DisconnectMsg, State0)
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad packet length "
+ ++ integer_to_list(PacketLen)},
+ StateName, D0)
catch
- _:_ ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad packet",
- language = ""},
- handle_disconnect(DisconnectMsg, State0)
+ _C:_E ->
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad packet"},
+ StateName, D0)
end;
-
-handle_info({CloseTag, _Socket}, _StateName,
- #state{transport_close_tag = CloseTag,
- ssh_params = #ssh{role = _Role, opts = _Opts}} = State) ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Connection closed",
- language = "en"},
- handle_disconnect(DisconnectMsg, State);
-
-handle_info({timeout, {_, From} = Request}, Statename,
- #state{connection_state = #connection{requests = Requests} = Connection} = State) ->
+
+
+%%%====
+handle_event(internal, prepare_next_packet, _, D) ->
+ Enough = erlang:max(8, D#data.ssh_params#ssh.decrypt_block_size),
+ case size(D#data.encrypted_data_buffer) of
+ Sz when Sz >= Enough ->
+ self() ! {D#data.transport_protocol, D#data.socket, <<>>};
+ _ ->
+ inet:setopts(D#data.socket, [{active, once}])
+ end,
+ keep_state_and_data;
+
+handle_event(info, {CloseTag,Socket}, StateName,
+ D = #data{socket = Socket,
+ transport_close_tag = CloseTag}) ->
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Connection closed"},
+ StateName, D);
+
+handle_event(info, {timeout, {_, From} = Request}, _,
+ #data{connection_state = #connection{requests = Requests} = C0} = D) ->
case lists:member(Request, Requests) of
true ->
- gen_fsm:reply(From, {error, timeout}),
- {next_state, Statename,
- State#state{connection_state =
- Connection#connection{requests =
- lists:delete(Request, Requests)}}};
+ %% A channel request is not answered in time. Answer {error,timeout}
+ %% to the caller
+ C = C0#connection{requests = lists:delete(Request, Requests)},
+ {keep_state, D#data{connection_state=C}, [{reply,From,{error,timeout}}]};
false ->
- {next_state, Statename, State}
+ %% The request is answered - just ignore the timeout
+ keep_state_and_data
end;
%%% Handle that ssh channels user process goes down
-handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, Statename, State0) ->
- {{replies, Replies}, State1} = handle_channel_down(ChannelPid, State0),
- State = send_replies(Replies, State1),
- {next_state, Statename, next_packet(State)};
+handle_event(info, {'DOWN', _Ref, process, ChannelPid, _Reason}, _, D0) ->
+ {{replies, Replies}, D1} = handle_channel_down(ChannelPid, D0),
+ {Repls, D} = send_replies(Replies, D1),
+ {keep_state, D, Repls};
%%% So that terminate will be run when supervisor is shutdown
-handle_info({'EXIT', _Sup, Reason}, _StateName, State) ->
- {stop, {shutdown, Reason}, State};
+handle_event(info, {'EXIT', _Sup, Reason}, _, _) ->
+ {stop, {shutdown, Reason}};
-handle_info({check_cache, _ , _},
- StateName, #state{connection_state =
- #connection{channel_cache = Cache}} = State) ->
- {next_state, StateName, check_cache(State, Cache)};
+handle_event(info, check_cache, _, D) ->
+ {keep_state, cache_check_set_idle_timer(D)};
-handle_info(UnexpectedMessage, StateName, #state{opts = Opts,
- ssh_params = SshParams} = State) ->
- case unexpected_fun(UnexpectedMessage, Opts, SshParams) of
+handle_event(info, UnexpectedMessage, StateName, D = #data{ssh_params = Ssh}) ->
+ case unexpected_fun(UnexpectedMessage, D) of
report ->
Msg = lists:flatten(
io_lib:format(
"Unexpected message '~p' received in state '~p'\n"
"Role: ~p\n"
"Peer: ~p\n"
- "Local Address: ~p\n", [UnexpectedMessage, StateName,
- SshParams#ssh.role, SshParams#ssh.peer,
- proplists:get_value(address, SshParams#ssh.opts)])),
- error_logger:info_report(Msg);
+ "Local Address: ~p\n", [UnexpectedMessage,
+ StateName,
+ Ssh#ssh.role,
+ Ssh#ssh.peer,
+ proplists:get_value(address, Ssh#ssh.opts)])),
+ error_logger:info_report(Msg),
+ keep_state_and_data;
skip ->
- ok;
+ keep_state_and_data;
Other ->
Msg = lists:flatten(
@@ -1103,200 +1269,181 @@ handle_info(UnexpectedMessage, StateName, #state{opts = Opts,
"Message: ~p\n"
"Role: ~p\n"
"Peer: ~p\n"
- "Local Address: ~p\n", [Other, UnexpectedMessage,
- SshParams#ssh.role,
- element(2,SshParams#ssh.peer),
- proplists:get_value(address, SshParams#ssh.opts)]
+ "Local Address: ~p\n", [Other,
+ UnexpectedMessage,
+ Ssh#ssh.role,
+ element(2,Ssh#ssh.peer),
+ proplists:get_value(address, Ssh#ssh.opts)]
)),
+ error_logger:error_report(Msg),
+ keep_state_and_data
+ end;
+
+handle_event(internal, {disconnect,Msg,_Reason}, StateName, D) ->
+ disconnect(Msg, StateName, D);
+
+handle_event(Type, Ev, StateName, D) ->
+ Descr =
+ case catch atom_to_list(element(1,Ev)) of
+ "ssh_msg_" ++_ when Type==internal ->
+ "Message in wrong state";
+ _ ->
+ "Internal error"
+ end,
+ disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = Descr},
+ StateName, D).
- error_logger:error_report(Msg)
- end,
- {next_state, StateName, State}.
%%--------------------------------------------------------------------
--spec terminate(Reason::term(), state_name(), #state{}) -> _.
-%%--------------------------------------------------------------------
-terminate(normal, _, #state{transport_cb = Transport,
- connection_state = Connection,
- socket = Socket}) ->
- terminate_subsystem(Connection),
- (catch Transport:close(Socket)),
- ok;
+-spec terminate(any(),
+ state_name(),
+ #data{}
+ ) -> finalize_termination_result() .
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+terminate(normal, StateName, State) ->
+ finalize_termination(StateName, State);
terminate({shutdown,{init,Reason}}, StateName, State) ->
error_logger:info_report(io_lib:format("Erlang ssh in connection handler init: ~p~n",[Reason])),
- terminate(normal, StateName, State);
-
-%% Terminated by supervisor
-terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Application shutdown",
- language = "en"},
- {SshPacket, Ssh} = ssh_transport:ssh_packet(DisconnectMsg, Ssh0),
- send_msg(SshPacket, State),
- terminate(normal, StateName, State#state{ssh_params = Ssh});
-
-terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName,
- #state{ssh_params = Ssh0} = State) ->
- {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
- send_msg(SshPacket, State),
- terminate(normal, StateName, State#state{ssh_params = Ssh});
-
-terminate({shutdown, _}, StateName, State) ->
- terminate(normal, StateName, State);
-
-terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid,
- connection_state = Connection} = State) ->
- terminate_subsystem(Connection),
+ finalize_termination(StateName, State);
+
+terminate(shutdown, StateName, State0) ->
+ %% Terminated by supervisor
+ State = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Application shutdown"},
+ State0),
+timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead
+ finalize_termination(StateName, State);
+
+%% terminate({shutdown,Msg}, StateName, State0) when is_record(Msg,ssh_msg_disconnect)->
+%% State = send_msg(Msg, State0),
+%% timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead
+%% finalize_termination(StateName, Msg, State);
+
+terminate({shutdown,_R}, StateName, State) ->
+ finalize_termination(StateName, State);
+
+terminate(Reason, StateName, State0) ->
+ %% Others, e.g undef, {badmatch,_}
log_error(Reason),
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Internal error",
- language = "en"},
- {SshPacket, Ssh} = ssh_transport:ssh_packet(DisconnectMsg, Ssh0),
- send_msg(SshPacket, State),
- terminate(normal, StateName, State#state{ssh_params = Ssh}).
-
-
-terminate_subsystem(#connection{system_supervisor = SysSup,
- sub_system_supervisor = SubSysSup}) when is_pid(SubSysSup) ->
- ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
-terminate_subsystem(_) ->
- ok.
-
-format_status(normal, [_, State]) ->
- [{data, [{"StateData", State}]}];
-format_status(terminate, [_, State]) ->
- SshParams0 = (State#state.ssh_params),
- SshParams = SshParams0#ssh{c_keyinit = "***",
- s_keyinit = "***",
- send_mac_key = "***",
- send_mac_size = "***",
- recv_mac_key = "***",
- recv_mac_size = "***",
- encrypt_keys = "***",
- encrypt_ctx = "***",
- decrypt_keys = "***",
- decrypt_ctx = "***",
- compress_ctx = "***",
- decompress_ctx = "***",
- shared_secret = "***",
- exchanged_hash = "***",
- session_id = "***",
- keyex_key = "***",
- keyex_info = "***",
- available_host_keys = "***"},
- [{data, [{"StateData", State#state{decoded_data_buffer = "***",
- encoded_data_buffer = "***",
- key_exchange_init_msg = "***",
- opts = "***",
- recbuf = "***",
- ssh_params = SshParams
- }}]}].
+ State = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Internal error"},
+ State0),
+ finalize_termination(StateName, State).
%%--------------------------------------------------------------------
--spec code_change(OldVsn::term(), state_name(), Oldstate::term(), Extra::term()) ->
- {ok, state_name(), #state{}}.
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+format_status(normal, [_, _StateName, D]) ->
+ [{data, [{"State", D}]}];
+format_status(terminate, [_, _StateName, D]) ->
+ DataPropList0 = fmt_stat_rec(record_info(fields, data), D,
+ [decrypted_data_buffer,
+ encrypted_data_buffer,
+ key_exchange_init_msg,
+ user_passwords,
+ opts,
+ inet_initial_recbuf_size]),
+ SshPropList = fmt_stat_rec(record_info(fields, ssh), D#data.ssh_params,
+ [c_keyinit,
+ s_keyinit,
+ send_mac_key,
+ send_mac_size,
+ recv_mac_key,
+ recv_mac_size,
+ encrypt_keys,
+ encrypt_ctx,
+ decrypt_keys,
+ decrypt_ctx,
+ compress_ctx,
+ decompress_ctx,
+ shared_secret,
+ exchanged_hash,
+ session_id,
+ keyex_key,
+ keyex_info,
+ available_host_keys]),
+ DataPropList = lists:keyreplace(ssh_params, 1, DataPropList0,
+ {ssh_params,SshPropList}),
+ [{data, [{"State", DataPropList}]}].
+
+
+fmt_stat_rec(FieldNames, Rec, Exclude) ->
+ Values = tl(tuple_to_list(Rec)),
+ [P || {K,_} = P <- lists:zip(FieldNames, Values),
+ not lists:member(K, Exclude)].
+
%%--------------------------------------------------------------------
+-spec code_change(term() | {down,term()},
+ state_name(),
+ #data{},
+ term()
+ ) -> {gen_statem:callback_mode(), state_name(), #data{}}.
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
code_change(_OldVsn, StateName, State, _Extra) ->
- {ok, StateName, State}.
+ {handle_event_function, StateName, State}.
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-init_role(#state{role = client, opts = Opts} = State0) ->
- Pid = proplists:get_value(user_pid, Opts),
- TimerRef = get_idle_time(Opts),
- timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]),
- timer:apply_after(?REKEY_DATA_TIMOUT, gen_fsm, send_all_state_event,
- [self(), data_size]),
- State0#state{starter = Pid,
- idle_timer_ref = TimerRef};
-init_role(#state{role = server, opts = Opts, connection_state = Connection} = State) ->
- Sups = proplists:get_value(supervisors, Opts),
- Pid = proplists:get_value(user_pid, Opts),
- SystemSup = proplists:get_value(system_sup, Sups),
- SubSystemSup = proplists:get_value(subsystem_sup, Sups),
+%% Starting
+
+start_the_connection_child(UserPid, Role, Socket, Options) ->
+ Sups = proplists:get_value(supervisors, Options),
ConnectionSup = proplists:get_value(connection_sup, Sups),
- Shell = proplists:get_value(shell, Opts),
- Exec = proplists:get_value(exec, Opts),
- CliSpec = proplists:get_value(ssh_cli, Opts, {ssh_cli, [Shell]}),
- State#state{starter = Pid, connection_state = Connection#connection{
- cli_spec = CliSpec,
- exec = Exec,
- system_supervisor = SystemSup,
- sub_system_supervisor = SubSystemSup,
- connection_supervisor = ConnectionSup
- }}.
-
-get_idle_time(SshOptions) ->
- case proplists:get_value(idle_time, SshOptions) of
- infinity ->
- infinity;
- _IdleTime -> %% We dont want to set the timeout on first connect
- undefined
- end.
+ Opts = [{supervisors, Sups}, {user_pid, UserPid} | proplists:get_value(ssh_opts, Options, [])],
+ {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
+ ok = socket_control(Socket, Pid, Options),
+ Pid.
-init_ssh(client = Role, Vsn, Version, Options, Socket) ->
- IOCb = case proplists:get_value(user_interaction, Options, true) of
- true ->
- ssh_io;
- false ->
- ssh_no_io
- end,
+%%--------------------------------------------------------------------
+%% Stopping
+-type finalize_termination_result() :: ok .
+
+finalize_termination(_StateName, #data{transport_cb = Transport,
+ connection_state = Connection,
+ socket = Socket}) ->
+ case Connection of
+ #connection{system_supervisor = SysSup,
+ sub_system_supervisor = SubSysSup} when is_pid(SubSysSup) ->
+ ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
+ _ ->
+ do_nothing
+ end,
+ (catch Transport:close(Socket)),
+ ok.
- AuthMethods = proplists:get_value(auth_methods, Options,
- ?SUPPORTED_AUTH_METHODS),
- {ok, PeerAddr} = inet:peername(Socket),
-
- PeerName = proplists:get_value(host, Options),
- KeyCb = proplists:get_value(key_cb, Options, ssh_file),
-
- #ssh{role = Role,
- c_vsn = Vsn,
- c_version = Version,
- key_cb = KeyCb,
- io_cb = IOCb,
- userauth_quiet_mode = proplists:get_value(quiet_mode, Options, false),
- opts = Options,
- userauth_supported_methods = AuthMethods,
- peer = {PeerName, PeerAddr},
- available_host_keys = supported_host_keys(Role, KeyCb, Options),
- random_length_padding = proplists:get_value(max_random_length_padding,
- Options,
- (#ssh{})#ssh.random_length_padding)
- };
-
-init_ssh(server = Role, Vsn, Version, Options, Socket) ->
- AuthMethods = proplists:get_value(auth_methods, Options,
- ?SUPPORTED_AUTH_METHODS),
- AuthMethodsAsList = string:tokens(AuthMethods, ","),
- {ok, PeerAddr} = inet:peername(Socket),
- KeyCb = proplists:get_value(key_cb, Options, ssh_file),
-
- #ssh{role = Role,
- s_vsn = Vsn,
- s_version = Version,
- key_cb = KeyCb,
- io_cb = proplists:get_value(io_cb, Options, ssh_io),
- opts = Options,
- userauth_supported_methods = AuthMethods,
- userauth_methods = AuthMethodsAsList,
- kb_tries_left = 3,
- peer = {undefined, PeerAddr},
- available_host_keys = supported_host_keys(Role, KeyCb, Options),
- random_length_padding = proplists:get_value(max_random_length_padding,
- Options,
- (#ssh{})#ssh.random_length_padding)
- }.
+%%--------------------------------------------------------------------
+%% "Invert" the Role
+peer_role(client) -> server;
+peer_role(server) -> client.
+
+%%--------------------------------------------------------------------
+%% StateName to Role
+role({_,Role}) -> Role;
+role({_,Role,_}) -> Role.
+%%--------------------------------------------------------------------
+%% Check the StateName to see if we are in the renegotiation phase
+renegotiation({_,_,ReNeg}) -> ReNeg == renegotiation;
+renegotiation(_) -> false.
+
+%%--------------------------------------------------------------------
supported_host_keys(client, _, Options) ->
try
- case proplists:get_value(public_key,
+ case proplists:get_value(public_key,
proplists:get_value(preferred_algorithms,Options,[])
) of
- undefined ->
+ undefined ->
ssh_transport:default_algorithms(public_key);
L ->
L -- (L--ssh_transport:default_algorithms(public_key))
@@ -1311,7 +1458,7 @@ supported_host_keys(client, _, Options) ->
{stop, {shutdown, Reason}}
end;
supported_host_keys(server, KeyCb, Options) ->
- [atom_to_list(A) || A <- proplists:get_value(public_key,
+ [atom_to_list(A) || A <- proplists:get_value(public_key,
proplists:get_value(preferred_algorithms,Options,[]),
ssh_transport:default_algorithms(public_key)
),
@@ -1322,10 +1469,16 @@ supported_host_keys(server, KeyCb, Options) ->
available_host_key(KeyCb, Alg, Opts) ->
element(1, catch KeyCb:host_key(Alg, Opts)) == ok.
-send_msg(Msg, #state{socket = Socket, transport_cb = Transport}) ->
- Transport:send(Socket, Msg).
-handle_version({2, 0} = NumVsn, StrVsn, Ssh0) ->
+send_msg(Msg, State=#data{ssh_params=Ssh0}) when is_tuple(Msg) ->
+ {Bytes, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
+ send_bytes(Bytes, State),
+ State#data{ssh_params=Ssh}.
+
+send_bytes(Bytes, #data{socket = Socket, transport_cb = Transport}) ->
+ Transport:send(Socket, Bytes).
+
+handle_version({2, 0} = NumVsn, StrVsn, Ssh0) ->
Ssh = counterpart_versions(NumVsn, StrVsn, Ssh0),
{ok, Ssh};
handle_version(_,_,_) ->
@@ -1336,419 +1489,185 @@ string_version(#ssh{role = client, c_version = Vsn}) ->
string_version(#ssh{role = server, s_version = Vsn}) ->
Vsn.
-send_event(FsmPid, Event) ->
- gen_fsm:send_event(FsmPid, Event).
-send_all_state_event(FsmPid, Event) ->
- gen_fsm:send_all_state_event(FsmPid, Event).
+cast(FsmPid, Event) ->
+ gen_statem:cast(FsmPid, Event).
-sync_send_all_state_event(FsmPid, Event) ->
- sync_send_all_state_event(FsmPid, Event, infinity).
+call(FsmPid, Event) ->
+ call(FsmPid, Event, infinity).
-sync_send_all_state_event(FsmPid, Event, Timeout) ->
- try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout) of
- {closed, _Channel} ->
+call(FsmPid, Event, Timeout) ->
+ try gen_statem:call(FsmPid, Event, Timeout) of
+ {closed, _R} ->
+ {error, closed};
+ {killed, _R} ->
{error, closed};
Result ->
Result
catch
- exit:{noproc, _} ->
+ exit:{noproc, _R} ->
{error, closed};
- exit:{normal, _} ->
+ exit:{normal, _R} ->
{error, closed};
- exit:{{shutdown, _},_} ->
+ exit:{{shutdown, _R},_} ->
{error, closed}
end.
-%% simulate send_all_state_event(self(), Event)
-event(#ssh_msg_disconnect{} = Event, StateName, State) ->
- handle_event(Event, StateName, State);
-event(#ssh_msg_ignore{} = Event, StateName, State) ->
- handle_event(Event, StateName, State);
-event(#ssh_msg_debug{} = Event, StateName, State) ->
- handle_event(Event, StateName, State);
-event(#ssh_msg_unimplemented{} = Event, StateName, State) ->
- handle_event(Event, StateName, State);
-%% simulate send_event(self(), Event)
-event(Event, StateName, State) ->
- try
- ?MODULE:StateName(Event, State)
+
+handle_connection_msg(Msg, StateName, State0 =
+ #data{starter = User,
+ connection_state = Connection0,
+ event_queue = Qev0}) ->
+ Renegotiation = renegotiation(StateName),
+ Role = role(StateName),
+ try ssh_connection:handle_msg(Msg, Connection0, Role) of
+ {{replies, Replies}, Connection} ->
+ case StateName of
+ {connected,_} ->
+ {Repls, State} = send_replies(Replies,
+ State0#data{connection_state=Connection}),
+ {keep_state, State, Repls};
+ _ ->
+ {ConnReplies, Replies} =
+ lists:splitwith(fun not_connected_filter/1, Replies),
+ {Repls, State} = send_replies(Replies,
+ State0#data{event_queue = Qev0 ++ ConnReplies}),
+ {keep_state, State, Repls}
+ end;
+
+ {noreply, Connection} ->
+ {keep_state, State0#data{connection_state = Connection}};
+
+ {disconnect, Reason0, {{replies, Replies}, Connection}} ->
+ {Repls,State} = send_replies(Replies, State0#data{connection_state = Connection}),
+ case {Reason0,Role} of
+ {{_, Reason}, client} when ((StateName =/= {connected,client}) and (not Renegotiation)) ->
+ User ! {self(), not_connected, Reason};
+ _ ->
+ ok
+ end,
+ {stop_and_reply, {shutdown,normal}, Repls, State#data{connection_state = Connection}}
+
catch
- throw:#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- throw:{ErrorToDisplay, #ssh_msg_disconnect{} = DisconnectMsg} ->
- handle_disconnect(DisconnectMsg, State, ErrorToDisplay);
- _C:_Error ->
- handle_disconnect(#ssh_msg_disconnect{code = error_code(StateName),
- description = "Invalid state",
- language = "en"}, State)
+ _:Error ->
+ {disconnect, _Reason, {{replies, Replies}, Connection}} =
+ ssh_connection:handle_msg(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Internal error"},
+ Connection0, Role),
+ {Repls,State} = send_replies(Replies, State0#data{connection_state = Connection}),
+ {stop_and_reply, {shutdown,Error}, Repls, State#data{connection_state = Connection}}
end.
-error_code(key_exchange) ->
- ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED;
-error_code(new_keys) ->
- ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED;
-error_code(_) ->
- ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE.
-
-generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
- #state{
- role = Role,
- starter = User,
- renegotiate = Renegotiation,
- connection_state = Connection0} = State0, EncData)
- when Byte == ?SSH_MSG_GLOBAL_REQUEST;
- Byte == ?SSH_MSG_REQUEST_SUCCESS;
- Byte == ?SSH_MSG_REQUEST_FAILURE;
- Byte == ?SSH_MSG_CHANNEL_OPEN;
- Byte == ?SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
- Byte == ?SSH_MSG_CHANNEL_OPEN_FAILURE;
- Byte == ?SSH_MSG_CHANNEL_WINDOW_ADJUST;
- Byte == ?SSH_MSG_CHANNEL_DATA;
- Byte == ?SSH_MSG_CHANNEL_EXTENDED_DATA;
- Byte == ?SSH_MSG_CHANNEL_EOF;
- Byte == ?SSH_MSG_CHANNEL_CLOSE;
- Byte == ?SSH_MSG_CHANNEL_REQUEST;
- Byte == ?SSH_MSG_CHANNEL_SUCCESS;
- Byte == ?SSH_MSG_CHANNEL_FAILURE ->
- try
- ssh_message:decode(Msg)
- of
- ConnectionMsg ->
- State1 = generate_event_new_state(State0, EncData),
- try ssh_connection:handle_msg(ConnectionMsg, Connection0, Role) of
- {{replies, Replies0}, Connection} ->
- if StateName == connected ->
- Replies = Replies0,
- State2 = State1;
- true ->
- {ConnReplies, Replies} =
- lists:splitwith(fun not_connected_filter/1, Replies0),
- Q = State1#state.event_queue ++ ConnReplies,
- State2 = State1#state{ event_queue = Q }
- end,
- State = send_replies(Replies, State2#state{connection_state = Connection}),
- {next_state, StateName, next_packet(State)};
- {noreply, Connection} ->
- {next_state, StateName, next_packet(State1#state{connection_state = Connection})};
- {disconnect, {_, Reason}, {{replies, Replies}, Connection}} when
- Role == client andalso ((StateName =/= connected) and (not Renegotiation)) ->
- State = send_replies(Replies, State1#state{connection_state = Connection}),
- User ! {self(), not_connected, Reason},
- {stop, {shutdown, normal},
- next_packet(State#state{connection_state = Connection})};
- {disconnect, _Reason, {{replies, Replies}, Connection}} ->
- State = send_replies(Replies, State1#state{connection_state = Connection}),
- {stop, {shutdown, normal}, State#state{connection_state = Connection}}
- catch
- _:Error ->
- {disconnect, _Reason, {{replies, Replies}, Connection}} =
- ssh_connection:handle_msg(
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Internal error",
- language = "en"}, Connection0, Role),
- State = send_replies(Replies, State1#state{connection_state = Connection}),
- {stop, {shutdown, Error}, State#state{connection_state = Connection}}
- end
- catch
- _:_ ->
- handle_disconnect(
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad packet received",
- language = ""}, State0)
- end;
-generate_event(Msg, StateName, State0, EncData) ->
- try
- Event = ssh_message:decode(set_prefix_if_trouble(Msg,State0)),
- State = generate_event_new_state(State0, EncData),
- case Event of
- #ssh_msg_kexinit{} ->
- %% We need payload for verification later.
- event({Event, Msg}, StateName, State);
- _ ->
- event(Event, StateName, State)
- end
- catch
- _C:_E ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Encountered unexpected input",
- language = "en"},
- handle_disconnect(DisconnectMsg, State0)
- end.
-
-
-set_prefix_if_trouble(Msg = <<?BYTE(Op),_/binary>>, #state{ssh_params=SshParams})
+set_kex_overload_prefix(Msg = <<?BYTE(Op),_/binary>>, #data{ssh_params=SshParams})
when Op == 30;
Op == 31
->
case catch atom_to_list(kex(SshParams)) of
- "ecdh-sha2-" ++ _ ->
+ "ecdh-sha2-" ++ _ ->
<<"ecdh",Msg/binary>>;
"diffie-hellman-group-exchange-" ++ _ ->
<<"dh_gex",Msg/binary>>;
"diffie-hellman-group" ++ _ ->
<<"dh",Msg/binary>>;
- _ ->
+ _ ->
Msg
end;
-set_prefix_if_trouble(Msg, _) ->
+set_kex_overload_prefix(Msg, _) ->
Msg.
kex(#ssh{algorithms=#alg{kex=Kex}}) -> Kex;
kex(_) -> undefined.
+cache(#data{connection_state=C}) -> C#connection.channel_cache.
+
-handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
+%%%----------------------------------------------------------------
+handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, D) ->
+ case ssh_channel:cache_lookup(cache(D), ChannelId) of
#channel{remote_id = Id} = Channel ->
- update_sys(Cache, Channel, Type, ChannelPid),
- Msg = ssh_connection:channel_request_msg(Id, Type,
- WantReply, Data),
- Replies = [{connection_reply, Msg}],
- State = add_request(WantReply, ChannelId, From, State0),
- {{replies, Replies}, State};
+ update_sys(cache(D), Channel, Type, ChannelPid),
+ send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
+ add_request(WantReply, ChannelId, From, D));
undefined ->
- {{replies, []}, State0}
+ D
end.
-handle_request(ChannelId, Type, Data, WantReply, From,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id} ->
- Msg = ssh_connection:channel_request_msg(Id, Type,
- WantReply, Data),
- Replies = [{connection_reply, Msg}],
- State = add_request(WantReply, ChannelId, From, State0),
- {{replies, Replies}, State};
+handle_request(ChannelId, Type, Data, WantReply, From, D) ->
+ case ssh_channel:cache_lookup(cache(D), ChannelId) of
+ #channel{remote_id = Id} ->
+ send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
+ add_request(WantReply, ChannelId, From, D));
undefined ->
- {{replies, []}, State0}
- end.
-
-handle_global_request({global_request, ChannelPid,
- "tcpip-forward" = Type, WantReply,
- <<?UINT32(IPLen),
- IP:IPLen/binary, ?UINT32(Port)>> = Data},
- #state{connection_state =
- #connection{channel_cache = Cache}
- = Connection0} = State) ->
- ssh_channel:cache_update(Cache, #channel{user = ChannelPid,
- type = "forwarded-tcpip",
- sys = none}),
- Connection = ssh_connection:bind(IP, Port, ChannelPid, Connection0),
- Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
- send_replies([{connection_reply, Msg}], State#state{connection_state = Connection});
-
-handle_global_request({global_request, _Pid, "cancel-tcpip-forward" = Type,
- WantReply, <<?UINT32(IPLen),
- IP:IPLen/binary, ?UINT32(Port)>> = Data},
- #state{connection_state = Connection0} = State) ->
- Connection = ssh_connection:unbind(IP, Port, Connection0),
- Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
- send_replies([{connection_reply, Msg}], State#state{connection_state = Connection});
-
-handle_global_request({global_request, _, "cancel-tcpip-forward" = Type,
- WantReply, Data}, State) ->
- Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
- send_replies([{connection_reply, Msg}], State).
-
-handle_idle_timeout(#state{opts = Opts}) ->
- case proplists:get_value(idle_time, Opts, infinity) of
- infinity ->
- ok;
- IdleTime ->
- erlang:send_after(IdleTime, self(), {check_cache, [], []})
+ D
end.
-handle_channel_down(ChannelPid, #state{connection_state =
- #connection{channel_cache = Cache}} =
- State) ->
+%%%----------------------------------------------------------------
+handle_channel_down(ChannelPid, D) ->
ssh_channel:cache_foldl(
fun(Channel, Acc) when Channel#channel.user == ChannelPid ->
- ssh_channel:cache_delete(Cache,
+ ssh_channel:cache_delete(cache(D),
Channel#channel.local_id),
Acc;
(_,Acc) ->
Acc
- end, [], Cache),
- {{replies, []}, check_cache(State, Cache)}.
+ end, [], cache(D)),
+ {{replies, []}, cache_check_set_idle_timer(D)}.
+
update_sys(Cache, Channel, Type, ChannelPid) ->
ssh_channel:cache_update(Cache,
Channel#channel{sys = Type, user = ChannelPid}).
+
add_request(false, _ChannelId, _From, State) ->
State;
-add_request(true, ChannelId, From, #state{connection_state =
- #connection{requests = Requests0} =
- Connection} = State) ->
+add_request(true, ChannelId, From, #data{connection_state =
+ #connection{requests = Requests0} =
+ Connection} = State) ->
Requests = [{ChannelId, From} | Requests0],
- State#state{connection_state = Connection#connection{requests = Requests}}.
+ State#data{connection_state = Connection#connection{requests = Requests}}.
-new_channel_id(#state{connection_state = #connection{channel_id_seed = Id} =
- Connection}
+new_channel_id(#data{connection_state = #connection{channel_id_seed = Id} =
+ Connection}
= State) ->
- {Id, State#state{connection_state =
- Connection#connection{channel_id_seed = Id + 1}}}.
-
-generate_event_new_state(#state{ssh_params =
- #ssh{recv_sequence = SeqNum0}
- = Ssh} = State, EncData) ->
- SeqNum = ssh_transport:next_seqnum(SeqNum0),
- State#state{ssh_params = Ssh#ssh{recv_sequence = SeqNum},
- decoded_data_buffer = <<>>,
- encoded_data_buffer = EncData,
- undecoded_packet_length = undefined}.
-
-next_packet(#state{decoded_data_buffer = <<>>,
- encoded_data_buffer = Buff,
- ssh_params = #ssh{decrypt_block_size = BlockSize},
- socket = Socket,
- transport_protocol = Protocol} = State) when Buff =/= <<>> ->
- case size(Buff) >= erlang:max(8, BlockSize) of
- true ->
- %% Enough data from the next packet has been received to
- %% decode the length indicator, fake a socket-recive
- %% message so that the data will be processed
- self() ! {Protocol, Socket, <<>>};
- false ->
- inet:setopts(Socket, [{active, once}])
- end,
- State;
-
-next_packet(#state{socket = Socket} = State) ->
- inet:setopts(Socket, [{active, once}]),
- State.
-
-after_new_keys(#state{renegotiate = true} = State) ->
- State1 = State#state{renegotiate = false, event_queue = []},
- lists:foldr(fun after_new_keys_events/2, {next_state, connected, State1}, State#state.event_queue);
-after_new_keys(#state{renegotiate = false,
- ssh_params = #ssh{role = client} = Ssh0} = State) ->
- {Msg, Ssh} = ssh_auth:service_request_msg(Ssh0),
- send_msg(Msg, State),
- {next_state, service_request, State#state{ssh_params = Ssh}};
-after_new_keys(#state{renegotiate = false,
- ssh_params = #ssh{role = server}} = State) ->
- {next_state, service_request, State}.
-
-after_new_keys_events({sync, _Event, From}, {stop, _Reason, _StateData}=Terminator) ->
- gen_fsm:reply(From, {error, closed}),
- Terminator;
-after_new_keys_events(_, {stop, _Reason, _StateData}=Terminator) ->
- Terminator;
-after_new_keys_events({sync, Event, From}, {next_state, StateName, StateData}) ->
- case handle_sync_event(Event, From, StateName, StateData) of
- {reply, Reply, NextStateName, NewStateData} ->
- gen_fsm:reply(From, Reply),
- {next_state, NextStateName, NewStateData};
- {next_state, NextStateName, NewStateData}->
- {next_state, NextStateName, NewStateData};
- {stop, Reason, Reply, NewStateData} ->
- gen_fsm:reply(From, Reply),
- {stop, Reason, NewStateData}
- end;
-after_new_keys_events({event, Event}, {next_state, StateName, StateData}) ->
- case handle_event(Event, StateName, StateData) of
- {next_state, NextStateName, NewStateData}->
- {next_state, NextStateName, NewStateData};
- {stop, Reason, NewStateData} ->
- {stop, Reason, NewStateData}
- end;
-after_new_keys_events({connection_reply, _Data} = Reply, {StateName, State}) ->
- NewState = send_replies([Reply], State),
- {next_state, StateName, NewState}.
-
-
-handle_disconnect(DisconnectMsg, State) ->
- handle_disconnect(own, DisconnectMsg, State).
-
-handle_disconnect(#ssh_msg_disconnect{} = DisconnectMsg, State, Error) ->
- handle_disconnect(own, DisconnectMsg, State, Error);
-handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0, role = Role} = State0) ->
- {disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
- State = send_replies(disconnect_replies(Type, Msg, Replies), State0),
- disconnect_fun(Desc, State#state.opts),
- {stop, {shutdown, Desc}, State#state{connection_state = Connection}}.
-
-handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
- role = Role} = State0, ErrorMsg) ->
- {disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
- State = send_replies(disconnect_replies(Type, Msg, Replies), State0),
- disconnect_fun(Desc, State#state.opts),
- {stop, {shutdown, {Desc, ErrorMsg}}, State#state{connection_state = Connection}}.
-
-disconnect_replies(own, Msg, Replies) ->
- [{connection_reply, Msg} | Replies];
-disconnect_replies(peer, _, Replies) ->
- Replies.
-
+ {Id, State#data{connection_state =
+ Connection#connection{channel_id_seed = Id + 1}}}.
+
+%%%----------------------------------------------------------------
+%% %%% This server/client has decided to disconnect via the state machine:
+disconnect(Msg=#ssh_msg_disconnect{description=Description}, _StateName, State0) ->
+ State = send_msg(Msg, State0),
+ disconnect_fun(Description, State),
+timer:sleep(400),
+ {stop, {shutdown,Description}, State}.
+
+%%%----------------------------------------------------------------
counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn};
counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}.
-opposite_role(client) ->
- server;
-opposite_role(server) ->
- client.
-connected_fun(User, PeerAddr, Method, Opts) ->
- case proplists:get_value(connectfun, Opts) of
- undefined ->
- ok;
- Fun ->
- catch Fun(User, PeerAddr, Method)
- end.
-
-retry_fun(_, _, undefined, _) ->
- ok;
-
-retry_fun(User, PeerAddr, {error, Reason}, Opts) ->
- case proplists:get_value(failfun, Opts) of
- undefined ->
- ok;
- Fun ->
- do_retry_fun(Fun, User, PeerAddr, Reason)
- end;
-
-retry_fun(User, PeerAddr, Reason, Opts) ->
- case proplists:get_value(infofun, Opts) of
- undefined ->
- ok;
- Fun ->
- do_retry_fun(Fun, User, PeerAddr, Reason)
- end.
-
-do_retry_fun(Fun, User, PeerAddr, Reason) ->
- case erlang:fun_info(Fun, arity) of
- {arity, 2} -> %% Backwards compatible
- catch Fun(User, Reason);
- {arity, 3} ->
- catch Fun(User, PeerAddr, Reason)
- end.
-
ssh_info([], _State, Acc) ->
Acc;
-ssh_info([client_version | Rest], #state{ssh_params = #ssh{c_vsn = IntVsn,
+ssh_info([client_version | Rest], #data{ssh_params = #ssh{c_vsn = IntVsn,
c_version = StringVsn}} = State, Acc) ->
ssh_info(Rest, State, [{client_version, {IntVsn, StringVsn}} | Acc]);
-ssh_info([server_version | Rest], #state{ssh_params =#ssh{s_vsn = IntVsn,
+ssh_info([server_version | Rest], #data{ssh_params =#ssh{s_vsn = IntVsn,
s_version = StringVsn}} = State, Acc) ->
ssh_info(Rest, State, [{server_version, {IntVsn, StringVsn}} | Acc]);
-ssh_info([peer | Rest], #state{ssh_params = #ssh{peer = Peer}} = State, Acc) ->
+ssh_info([peer | Rest], #data{ssh_params = #ssh{peer = Peer}} = State, Acc) ->
ssh_info(Rest, State, [{peer, Peer} | Acc]);
-ssh_info([sockname | Rest], #state{socket = Socket} = State, Acc) ->
+ssh_info([sockname | Rest], #data{socket = Socket} = State, Acc) ->
{ok, SockName} = inet:sockname(Socket),
ssh_info(Rest, State, [{sockname, SockName}|Acc]);
-ssh_info([user | Rest], #state{auth_user = User} = State, Acc) ->
+ssh_info([user | Rest], #data{auth_user = User} = State, Acc) ->
ssh_info(Rest, State, [{user, User}|Acc]);
ssh_info([ _ | Rest], State, Acc) ->
ssh_info(Rest, State, Acc).
+
ssh_channel_info([], _, Acc) ->
Acc;
@@ -1765,43 +1684,49 @@ ssh_channel_info([send_window | Rest], #channel{send_window_size = WinSize,
ssh_channel_info([ _ | Rest], Channel, Acc) ->
ssh_channel_info(Rest, Channel, Acc).
+
log_error(Reason) ->
- Report = io_lib:format("Erlang ssh connection handler failed with reason: "
- "~p ~n, Stacktrace: ~p ~n",
- [Reason, erlang:get_stacktrace()]),
- error_logger:error_report(Report),
- "Internal error".
-
-not_connected_filter({connection_reply, _Data}) ->
- true;
-not_connected_filter(_) ->
- false.
-
-send_replies([], State) ->
- State;
-send_replies([{connection_reply, Data} | Rest], #state{ssh_params = Ssh0} = State) ->
- {Packet, Ssh} = ssh_transport:ssh_packet(Data, Ssh0),
- send_msg(Packet, State),
- send_replies(Rest, State#state{ssh_params = Ssh});
-send_replies([Msg | Rest], State) ->
- catch send_reply(Msg),
- send_replies(Rest, State).
-
-send_reply({channel_data, Pid, Data}) ->
- Pid ! {ssh_cm, self(), Data};
-send_reply({channel_requst_reply, From, Data}) ->
- gen_fsm:reply(From, Data);
-send_reply({flow_control, Cache, Channel, From, Msg}) ->
+ Report = io_lib:format("Erlang ssh connection handler failed with reason:~n"
+ " ~p~n"
+ "Stacktrace:~n"
+ " ~p~n",
+ [Reason, erlang:get_stacktrace()]),
+ error_logger:error_report(Report).
+
+
+%%%----------------------------------------------------------------
+not_connected_filter({connection_reply, _Data}) -> true;
+not_connected_filter(_) -> false.
+
+%%%----------------------------------------------------------------
+send_replies(Repls, State) ->
+ lists:foldl(fun get_repl/2,
+ {[],State},
+ Repls).
+
+get_repl({connection_reply,Msg}, {CallRepls,S}) ->
+ {CallRepls, send_msg(Msg,S)};
+get_repl({channel_data,undefined,_Data}, Acc) ->
+ Acc;
+get_repl({channel_data,Pid,Data}, Acc) ->
+ Pid ! {ssh_cm, self(), Data},
+ Acc;
+get_repl({channel_request_reply,From,Data}, {CallRepls,S}) ->
+ {[{reply,From,Data}|CallRepls], S};
+get_repl({flow_control,Cache,Channel,From,Msg}, {CallRepls,S}) ->
ssh_channel:cache_update(Cache, Channel#channel{flow_control = undefined}),
- gen_fsm:reply(From, Msg);
-send_reply({flow_control, From, Msg}) ->
- gen_fsm:reply(From, Msg).
+ {[{reply,From,Msg}|CallRepls], S};
+get_repl({flow_control,From,Msg}, {CallRepls,S}) ->
+ {[{reply,From,Msg}|CallRepls], S};
+get_repl(noreply, Acc) ->
+ Acc;
+get_repl(X, Acc) ->
+ exit({get_repl,X,Acc}).
-disconnect_fun({disconnect,Msg}, Opts) ->
- disconnect_fun(Msg, Opts);
-disconnect_fun(_, undefined) ->
- ok;
-disconnect_fun(Reason, Opts) ->
+%%%----------------------------------------------------------------
+disconnect_fun({disconnect,Msg}, D) ->
+ disconnect_fun(Msg, D);
+disconnect_fun(Reason, #data{opts=Opts}) ->
case proplists:get_value(disconnectfun, Opts) of
undefined ->
ok;
@@ -1809,50 +1734,137 @@ disconnect_fun(Reason, Opts) ->
catch Fun(Reason)
end.
-unexpected_fun(UnexpectedMessage, Opts, #ssh{peer={_,Peer}}) ->
+unexpected_fun(UnexpectedMessage, #data{opts = Opts,
+ ssh_params = #ssh{peer = {_,Peer} }
+ } ) ->
case proplists:get_value(unexpectedfun, Opts) of
undefined ->
report;
Fun ->
- catch Fun(UnexpectedMessage, Peer)
+ catch Fun(UnexpectedMessage, Peer)
end.
-check_cache(#state{opts = Opts} = State, Cache) ->
- %% Check the number of entries in Cache
- case proplists:get_value(size, ets:info(Cache)) of
- 0 ->
- case proplists:get_value(idle_time, Opts, infinity) of
- infinity ->
- State;
- Time ->
- handle_idle_timer(Time, State)
- end;
+debug_fun(#ssh_msg_debug{always_display = Display,
+ message = DbgMsg,
+ language = Lang},
+ #data{opts = Opts}) ->
+ case proplists:get_value(ssh_msg_debug_fun, Opts) of
+ undefined ->
+ ok;
+ Fun ->
+ catch Fun(self(), Display, DbgMsg, Lang)
+ end.
+
+
+connected_fun(User, Method, #data{ssh_params = #ssh{peer = {_,Peer}},
+ opts = Opts}) ->
+ case proplists:get_value(connectfun, Opts) of
+ undefined ->
+ ok;
+ Fun ->
+ catch Fun(User, Peer, Method)
+ end.
+
+retry_fun(_, undefined, _) ->
+ ok;
+retry_fun(User, Reason, #data{ssh_params = #ssh{opts = Opts,
+ peer = {_,Peer}
+ }}) ->
+ {Tag,Info} =
+ case Reason of
+ {error, Error} ->
+ {failfun, Error};
+ _ ->
+ {infofun, Reason}
+ end,
+ Fun = proplists:get_value(Tag, Opts, fun(_,_)-> ok end),
+ try erlang:fun_info(Fun, arity)
+ of
+ {arity, 2} -> %% Backwards compatible
+ catch Fun(User, Info);
+ {arity, 3} ->
+ catch Fun(User, Peer, Info);
_ ->
- State
+ ok
+ catch
+ _:_ ->
+ ok
+ end.
+
+%%%----------------------------------------------------------------
+%%% Cache idle timer that closes the connection if there are no
+%%% channels open for a while.
+
+cache_init_idle_timer(D) ->
+ case proplists:get_value(idle_time, D#data.opts, infinity) of
+ infinity ->
+ D#data{idle_timer_value = infinity,
+ idle_timer_ref = infinity % A flag used later...
+ };
+ IdleTime ->
+ %% We dont want to set the timeout on first connect
+ D#data{idle_timer_value = IdleTime}
end.
-handle_idle_timer(Time, #state{idle_timer_ref = undefined} = State) ->
- TimerRef = erlang:send_after(Time, self(), {'EXIT', [], "Timeout"}),
- State#state{idle_timer_ref=TimerRef};
-handle_idle_timer(_, State) ->
- State.
-
-remove_timer_ref(State) ->
- case State#state.idle_timer_ref of
- infinity -> %% If the timer is not activated
- State;
- undefined -> %% If we already has cancelled the timer
- State;
- TimerRef -> %% Timer is active
+
+cache_check_set_idle_timer(D = #data{idle_timer_ref = undefined,
+ idle_timer_value = IdleTime}) ->
+ %% No timer set - shall we set one?
+ case ssh_channel:cache_info(num_entries, cache(D)) of
+ 0 when IdleTime == infinity ->
+ %% No. Meaningless to set a timer that fires in an infinite time...
+ D;
+ 0 ->
+ %% Yes, we'll set one since the cache is empty and it should not
+ %% be that for a specified time
+ D#data{idle_timer_ref =
+ erlang:send_after(IdleTime, self(), {'EXIT',[],"Timeout"})};
+ _ ->
+ %% No - there are entries in the cache
+ D
+ end;
+cache_check_set_idle_timer(D) ->
+ %% There is already a timer set or the timeout time is infinite
+ D.
+
+
+cache_cancel_idle_timer(D) ->
+ case D#data.idle_timer_ref of
+ infinity ->
+ %% The timer is not activated
+ D;
+ undefined ->
+ %% The timer is already cancelled
+ D;
+ TimerRef ->
+ %% The timer is active
erlang:cancel_timer(TimerRef),
- State#state{idle_timer_ref = undefined}
+ D#data{idle_timer_ref = undefined}
end.
-socket_control(Socket, Pid, Transport) ->
- case Transport:controlling_process(Socket, Pid) of
+
+cache_request_idle_timer_check(D = #data{idle_timer_value = infinity}) ->
+ D;
+cache_request_idle_timer_check(D = #data{idle_timer_value = IdleTime}) ->
+ erlang:send_after(IdleTime, self(), check_cache),
+ D.
+
+%%%----------------------------------------------------------------
+start_channel_request_timer(_,_, infinity) ->
+ ok;
+start_channel_request_timer(Channel, From, Time) ->
+ erlang:send_after(Time, self(), {timeout, {Channel, From}}).
+
+%%%----------------------------------------------------------------
+%%% Connection start and initalization helpers
+
+socket_control(Socket, Pid, Options) ->
+ {_, TransportCallback, _} = % For example {_,gen_tcp,_}
+ proplists:get_value(transport, Options, ?DefaultTransport),
+ case TransportCallback:controlling_process(Socket, Pid) of
ok ->
- send_event(Pid, socket_control);
+ gen_statem:cast(Pid, socket_control);
{error, Reason} ->
{error, Reason}
end.
@@ -1881,16 +1893,3 @@ handshake(Pid, Ref, Timeout) ->
{error, timeout}
end.
-start_timeout(_,_, infinity) ->
- ok;
-start_timeout(Channel, From, Time) ->
- erlang:send_after(Time, self(), {timeout, {Channel, From}}).
-
-getopt(Opt, Socket) ->
- case inet:getopts(Socket, [Opt]) of
- {ok, [{Opt, Value}]} ->
- {ok, Value};
- Other ->
- {error, {unexpected_getopts_return, Other}}
- end.
-
diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl
index 67130d5eac..0c24c09887 100644
--- a/lib/ssh/src/ssh_info.erl
+++ b/lib/ssh/src/ssh_info.erl
@@ -37,7 +37,7 @@ print() ->
io:format("~s", [string()]).
print(File) when is_list(File) ->
- {ok,D} = file:open(File, write),
+ {ok,D} = file:open(File, [write]),
print(D),
file:close(D);
print(D) ->
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 65754956aa..db80d4c9e3 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -50,13 +50,7 @@
-define(Empint(X), (ssh_bits:mpint(X))/binary ).
-define(Ebinary(X), ?STRING(X) ).
-%% encode(Msg) ->
-%% try encode1(Msg)
-%% catch
-%% C:E ->
-%% io:format('***********************~n~p:~p ~p~n',[C,E,Msg]),
-%% error(E)
-%% end.
+-define(unicode_list(B), unicode:characters_to_list(B)).
encode(#ssh_msg_global_request{
name = Name,
@@ -176,7 +170,7 @@ encode(#ssh_msg_userauth_pk_ok{
encode(#ssh_msg_userauth_passwd_changereq{prompt = Prompt,
languge = Lang
})->
- <<?Ebyte(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?Estring(Prompt), ?Estring(Lang)>>;
+ <<?Ebyte(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?Estring_utf8(Prompt), ?Estring(Lang)>>;
encode(#ssh_msg_userauth_info_request{
name = Name,
@@ -184,14 +178,14 @@ encode(#ssh_msg_userauth_info_request{
language_tag = Lang,
num_prompts = NumPromtps,
data = Data}) ->
- <<?Ebyte(?SSH_MSG_USERAUTH_INFO_REQUEST), ?Estring(Name), ?Estring(Inst), ?Estring(Lang),
+ <<?Ebyte(?SSH_MSG_USERAUTH_INFO_REQUEST), ?Estring_utf8(Name), ?Estring_utf8(Inst), ?Estring(Lang),
?Euint32(NumPromtps), ?'E...'(Data)>>;
encode(#ssh_msg_userauth_info_response{
num_responses = Num,
data = Data}) ->
lists:foldl(fun %%("", Acc) -> Acc; % commented out since it seem wrong
- (Response, Acc) -> <<Acc/binary, ?Estring(Response)>>
+ (Response, Acc) -> <<Acc/binary, ?Estring_utf8(Response)>>
end,
<<?Ebyte(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?Euint32(Num)>>,
Data);
@@ -201,17 +195,17 @@ encode(#ssh_msg_disconnect{
description = Desc,
language = Lang
}) ->
- <<?Ebyte(?SSH_MSG_DISCONNECT), ?Euint32(Code), ?Estring(Desc), ?Estring(Lang)>>;
+ <<?Ebyte(?SSH_MSG_DISCONNECT), ?Euint32(Code), ?Estring_utf8(Desc), ?Estring(Lang)>>;
encode(#ssh_msg_service_request{
name = Service
}) ->
- <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring(Service)>>;
+ <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring_utf8(Service)>>;
encode(#ssh_msg_service_accept{
name = Service
}) ->
- <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring(Service)>>;
+ <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring_utf8(Service)>>;
encode(#ssh_msg_newkeys{}) ->
<<?Ebyte(?SSH_MSG_NEWKEYS)>>;
@@ -283,7 +277,7 @@ encode(#ssh_msg_kex_ecdh_reply{public_host_key = Key, q_s = Q_s, h_sig = Sign})
<<?Ebyte(?SSH_MSG_KEX_ECDH_REPLY), ?Ebinary(EncKey), ?Empint(Q_s), ?Ebinary(EncSign)>>;
encode(#ssh_msg_ignore{data = Data}) ->
- <<?Ebyte(?SSH_MSG_IGNORE), ?Estring(Data)>>;
+ <<?Ebyte(?SSH_MSG_IGNORE), ?Estring_utf8(Data)>>;
encode(#ssh_msg_unimplemented{sequence = Seq}) ->
<<?Ebyte(?SSH_MSG_UNIMPLEMENTED), ?Euint32(Seq)>>;
@@ -291,7 +285,7 @@ encode(#ssh_msg_unimplemented{sequence = Seq}) ->
encode(#ssh_msg_debug{always_display = Bool,
message = Msg,
language = Lang}) ->
- <<?Ebyte(?SSH_MSG_DEBUG), ?Eboolean(Bool), ?Estring(Msg), ?Estring(Lang)>>.
+ <<?Ebyte(?SSH_MSG_DEBUG), ?Eboolean(Bool), ?Estring_utf8(Msg), ?Estring(Lang)>>.
%% Connection Messages
@@ -330,7 +324,7 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_FAILURE), ?UINT32(Recipient), ?UINT32(Reas
#ssh_msg_channel_open_failure{
recipient_channel = Recipient,
reason = Reason,
- description = unicode:characters_to_list(Desc),
+ description = ?unicode_list(Desc),
lang = Lang
};
decode(<<?BYTE(?SSH_MSG_CHANNEL_WINDOW_ADJUST), ?UINT32(Recipient), ?UINT32(Bytes)>>) ->
@@ -363,7 +357,7 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_REQUEST), ?UINT32(Recipient),
?DEC_BIN(RequestType,__0), ?BYTE(Bool), Data/binary>>) ->
#ssh_msg_channel_request{
recipient_channel = Recipient,
- request_type = unicode:characters_to_list(RequestType),
+ request_type = ?unicode_list(RequestType),
want_reply = erl_boolean(Bool),
data = Data
};
@@ -381,9 +375,9 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_REQUEST),
?DEC_BIN(User,__0), ?DEC_BIN(Service,__1), ?DEC_BIN(Method,__2),
Data/binary>>) ->
#ssh_msg_userauth_request{
- user = unicode:characters_to_list(User),
- service = unicode:characters_to_list(Service),
- method = unicode:characters_to_list(Method),
+ user = ?unicode_list(User),
+ service = ?unicode_list(Service),
+ method = ?unicode_list(Method),
data = Data
};
@@ -391,7 +385,7 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_FAILURE),
?DEC_BIN(Auths,__0),
?BYTE(Bool)>>) ->
#ssh_msg_userauth_failure {
- authentications = unicode:characters_to_list(Auths),
+ authentications = ?unicode_list(Auths),
partial_success = erl_boolean(Bool)
};
@@ -493,18 +487,18 @@ decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_REPLY),
decode(<<?SSH_MSG_SERVICE_REQUEST, ?DEC_BIN(Service,__0)>>) ->
#ssh_msg_service_request{
- name = unicode:characters_to_list(Service)
+ name = ?unicode_list(Service)
};
decode(<<?SSH_MSG_SERVICE_ACCEPT, ?DEC_BIN(Service,__0)>>) ->
#ssh_msg_service_accept{
- name = unicode:characters_to_list(Service)
+ name = ?unicode_list(Service)
};
decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1)>>) ->
#ssh_msg_disconnect{
code = Code,
- description = unicode:characters_to_list(Desc),
+ description = ?unicode_list(Desc),
language = Lang
};
@@ -512,7 +506,7 @@ decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0), ?DEC_BIN
decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0)>>) ->
#ssh_msg_disconnect{
code = Code,
- description = unicode:characters_to_list(Desc),
+ description = ?unicode_list(Desc),
language = <<"en">>
};
@@ -554,7 +548,7 @@ decode_kex_init(<<?BYTE(Bool)>>, Acc, 0) ->
X = 0,
list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) ->
- Names = string:tokens(unicode:characters_to_list(Data), ","),
+ Names = string:tokens(?unicode_list(Data), ","),
decode_kex_init(Rest, [Names | Acc], N -1).
diff --git a/lib/ssh/src/ssh_no_io.erl b/lib/ssh/src/ssh_no_io.erl
index 8144aac66e..1da257ed99 100644
--- a/lib/ssh/src/ssh_no_io.erl
+++ b/lib/ssh/src/ssh_no_io.erl
@@ -27,27 +27,39 @@
-export([yes_no/2, read_password/2, read_line/2, format/2]).
+
+-spec yes_no(any(), any()) -> no_return().
+
yes_no(_, _) ->
- throw({{no_io_allowed, yes_no},
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = "User interaction is not allowed",
- language = "en"}}).
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed"},
+ {no_io_allowed, yes_no}).
+
+
+-spec read_password(any(), any()) -> no_return().
read_password(_, _) ->
- throw({{no_io_allowed, read_password},
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = "User interaction is not allowed",
- language = "en"}}).
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed"},
+ {no_io_allowed, read_password}).
+
+
+-spec read_line(any(), any()) -> no_return().
read_line(_, _) ->
- throw({{no_io_allowed, read_line},
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = "User interaction is not allowed",
- language = "en"}} ).
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed"},
+ {no_io_allowed, read_line}).
+
+
+-spec format(any(), any()) -> no_return().
format(_, _) ->
- throw({{no_io_allowed, format},
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = "User interaction is not allowed",
- language = "en"}}).
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed"},
+ {no_io_allowed, format}).
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index 6314671f0d..9a9786a914 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -192,6 +192,9 @@ stop_acceptor(Sup) ->
[{Name, AcceptorSup}] =
[{SupName, ASup} || {SupName, ASup, _, [ssh_acceptor_sup]} <-
supervisor:which_children(Sup)],
- supervisor:terminate_child(AcceptorSup, Name).
-
-
+ case supervisor:terminate_child(AcceptorSup, Name) of
+ ok ->
+ supervisor:delete_child(AcceptorSup, Name);
+ Error ->
+ Error
+ end.
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index c04bd350c7..7cb3b75ac0 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -265,7 +265,8 @@ new_keys_message(Ssh0) ->
{SshPacket, Ssh} =
ssh_packet(#ssh_msg_newkeys{}, Ssh0),
{ok, SshPacket, Ssh}.
-
+
+
handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
#ssh{role = client} = Ssh0) ->
{ok, Algoritms} = select_algorithm(client, Own, CounterPart),
@@ -275,10 +276,10 @@ handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
Ssh0#ssh{algorithms = Algoritms});
_ ->
%% TODO: Correct code?
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Selection of key exchange"
- " algorithm failed",
- language = ""})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Selection of key exchange algorithm failed"
+ })
end;
handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
@@ -288,10 +289,10 @@ handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
true ->
{ok, Ssh#ssh{algorithms = Algoritms}};
_ ->
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Selection of key exchange"
- " algorithm failed",
- language = ""})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Selection of key exchange algorithm failed"
+ })
end.
@@ -371,12 +372,12 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E},
session_id = sid(Ssh1, H)}};
true ->
- throw({{error,bad_e_from_peer},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'e' out of bounds",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'e' out of bounds"},
+ {error,bad_e_from_peer}
+ )
end.
handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey,
@@ -396,21 +397,20 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey,
exchanged_hash = H,
session_id = sid(Ssh, H)}};
Error ->
- throw({Error,
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed",
- language = "en"}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed"},
+ Error)
end;
true ->
- throw({{error,bad_f_from_peer},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'f' out of bounds",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'f' out of bounds"},
+ bad_f_from_peer
+ )
end.
@@ -435,10 +435,11 @@ handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = Min0,
keyex_info = {Min, Max, NBits}
}};
{error,_} ->
- throw(#ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "No possible diffie-hellman-group-exchange group found",
- language = ""})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "No possible diffie-hellman-group-exchange group found"
+ })
end;
handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request_old{n = NBits},
@@ -469,19 +470,19 @@ handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request_old{n = NBits},
keyex_info = {-1, -1, NBits} % flag for kex_h hash calc
}};
{error,_} ->
- throw(#ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "No possible diffie-hellman-group-exchange group found",
- language = ""})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "No possible diffie-hellman-group-exchange group found"
+ })
end;
handle_kex_dh_gex_request(_, _) ->
- throw({{error,bad_ssh_msg_kex_dh_gex_request},
+ ssh_connection_handler:disconnect(
#ssh_msg_disconnect{
code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, bad values in ssh_msg_kex_dh_gex_request",
- language = ""}
- }).
+ description = "Key exchange failed, bad values in ssh_msg_kex_dh_gex_request"},
+ bad_ssh_msg_kex_dh_gex_request).
adjust_gex_min_max(Min0, Max0, Opts) ->
@@ -495,10 +496,11 @@ adjust_gex_min_max(Min0, Max0, Opts) ->
Min2 =< Max2 ->
{Min2, Max2};
Max2 < Min2 ->
- throw(#ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "No possible diffie-hellman-group-exchange group possible",
- language = ""})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "No possible diffie-hellman-group-exchange group possible"
+ })
end
end.
@@ -535,20 +537,18 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E},
session_id = sid(Ssh, H)
}};
true ->
- throw({{error,bad_K},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'K' out of bounds",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'K' out of bounds"},
+ bad_K)
end;
true ->
- throw({{error,bad_e_from_peer},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'e' out of bounds",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'e' out of bounds"},
+ bad_e_from_peer)
end.
handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostKey,
@@ -572,29 +572,28 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostK
exchanged_hash = H,
session_id = sid(Ssh, H)}};
_Error ->
- throw(#ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed",
- language = ""}
- )
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed"
+ })
end;
true ->
- throw({{error,bad_K},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'K' out of bounds",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'K' out of bounds"},
+ bad_K)
end;
true ->
- throw({{error,bad_f_from_peer},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'f' out of bounds",
- language = ""}
- })
- end.
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'f' out of bounds"},
+ bad_f_from_peer
+ )
+ end.
%%%----------------------------------------------------------------
%%%
@@ -624,12 +623,11 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic},
session_id = sid(Ssh1, H)}}
catch
_:_ ->
- throw({{error,invalid_peer_public_key},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Peer ECDH public key is invalid",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Peer ECDH public key is invalid"},
+ invalid_peer_public_key)
end.
handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey,
@@ -650,21 +648,19 @@ handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey,
exchanged_hash = H,
session_id = sid(Ssh, H)}};
Error ->
- throw({Error,
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed"},
+ Error)
end
catch
_:_ ->
- throw({{error,invalid_peer_public_key},
- #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Peer ECDH public key is invalid",
- language = ""}
- })
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Peer ECDH public key is invalid"},
+ invalid_peer_public_key)
end.
@@ -675,9 +671,10 @@ handle_new_keys(#ssh_msg_newkeys{}, Ssh0) ->
{ok, Ssh}
catch
_C:_Error -> %% TODO: Throw earlier ....
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Install alg failed",
- language = "en"})
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Install alg failed"
+ })
end.
%% select session id
@@ -929,9 +926,9 @@ select_all(CL, SL) when length(CL) + length(SL) < ?MAX_NUM_ALGORITHMS ->
lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A));
select_all(CL, SL) ->
Err = lists:concat(["Received too many algorithms (",length(CL),"+",length(SL)," >= ",?MAX_NUM_ALGORITHMS,")."]),
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = Err,
- language = ""}).
+ ssh_connection_handler:disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = Err}).
select([], []) ->
@@ -1042,7 +1039,7 @@ handle_packet_part(DecryptedPfx, EncryptedBuffer, TotalNeeded,
{bad_mac, Ssh1};
true ->
{Ssh, DecompressedPayload} = decompress(Ssh1, payload(DecryptedPacket)),
- {decoded, DecompressedPayload, NextPacketBytes, Ssh}
+ {packet_decrypted, DecompressedPayload, NextPacketBytes, Ssh}
end;
aead ->
PacketLenBin = DecryptedPfx,
@@ -1052,7 +1049,7 @@ handle_packet_part(DecryptedPfx, EncryptedBuffer, TotalNeeded,
{Ssh1, DecryptedSfx} ->
DecryptedPacket = <<DecryptedPfx/binary, DecryptedSfx/binary>>,
{Ssh, DecompressedPayload} = decompress(Ssh1, payload(DecryptedPacket)),
- {decoded, DecompressedPayload, NextPacketBytes, Ssh}
+ {packet_decrypted, DecompressedPayload, NextPacketBytes, Ssh}
end
end.
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 5667fd2aec..71b5c2c46a 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -64,7 +64,7 @@ child_spec(_) ->
Name = undefined, % As simple_one_for_one is used.
StartFunc = {ssh_connection_handler, start_link, []},
Restart = temporary,
- Shutdown = infinity,
+ Shutdown = 4000,
Modules = [ssh_connection_handler],
- Type = supervisor,
+ Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 4ecc662c13..6ce6d6f537 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -55,6 +55,7 @@ MODULES= \
ssh_relay
HRL_FILES_NEEDED_IN_TEST= \
+ $(ERL_TOP)/lib/ssh/test/ssh_test_lib.hrl \
$(ERL_TOP)/lib/ssh/src/ssh.hrl \
$(ERL_TOP)/lib/ssh/src/ssh_xfer.hrl
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index bdc980e65c..9910b8f1d7 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -192,7 +192,7 @@ simple_exec_groups_no_match_too_large(Config) ->
%%--------------------------------------------------------------------
%% Testing all default groups
-simple_exec_groups() -> [{timetrap,{seconds,180}}].
+simple_exec_groups() -> [{timetrap,{minutes,5}}].
simple_exec_groups(Config) ->
Sizes = interpolate( public_key:dh_gex_group_sizes() ),
@@ -226,28 +226,13 @@ sshc_simple_exec(Config) ->
KnownHosts = filename:join(PrivDir, "known_hosts"),
{Host,Port} = ?config(srvr_addr, Config),
Cmd = lists:concat(["ssh -p ",Port,
- " -C -o UserKnownHostsFile=",KnownHosts,
+ " -C",
+ " -o UserKnownHostsFile=",KnownHosts,
+ " -o StrictHostKeyChecking=no",
" ",Host," 1+1."]),
ct:log("~p",[Cmd]),
- SshPort = open_port({spawn, Cmd}, [binary]),
- Expect = <<"2\n">>,
- rcv_expected(SshPort, Expect).
-
-
-rcv_expected(SshPort, Expect) ->
- receive
- {SshPort, {data,Expect}} ->
- ct:log("Got expected ~p from ~p",[Expect,SshPort]),
- catch port_close(SshPort),
- ok;
- Other ->
- ct:log("Got UNEXPECTED ~p",[Other]),
- rcv_expected(SshPort, Expect)
-
- after ?TIMEOUT ->
- catch port_close(SshPort),
- ct:fail("Did not receive answer")
- end.
+ OpenSsh = ssh_test_lib:open_port({spawn, Cmd}, [eof,exit_status]),
+ ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT).
%%--------------------------------------------------------------------
%% Connect to the ssh server of the OS
@@ -361,13 +346,15 @@ get_atoms(L) ->
%%% Test case related
%%%
start_std_daemon(Opts, Config) ->
+ ct:log("starting std_daemon",[]),
{Pid, Host, Port} = ssh_test_lib:std_daemon(Config, Opts),
ct:log("started ~p:~p ~p",[Host,Port,Opts]),
[{srvr_pid,Pid},{srvr_addr,{Host,Port}} | Config].
-start_pubkey_daemon(Opts, Config) ->
+start_pubkey_daemon(Opts0, Config) ->
+ Opts = [{auth_methods,"publickey"}|Opts0],
{Pid, Host, Port} = ssh_test_lib:std_daemon1(Config, Opts),
- ct:log("started1 ~p:~p ~p",[Host,Port,Opts]),
+ ct:log("started pubkey_daemon ~p:~p ~p",[Host,Port,Opts]),
[{srvr_pid,Pid},{srvr_addr,{Host,Port}} | Config].
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl
index 8ec1017642..d9be1a32b7 100644
--- a/lib/ssh/test/ssh_benchmark_SUITE.erl
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -104,7 +104,7 @@ init_sftp_dirs(Config) ->
DstDir = filename:join(UserDir, "sftp_dst"),
ok = file:make_dir(DstDir),
N = 100 * 1024*1024,
- ok = file:write_file(filename:join(SrcDir,SrcFile), crypto:rand_bytes(N)),
+ ok = file:write_file(filename:join(SrcDir,SrcFile), crypto:strong_rand_bytes(N)),
[{sftp_src_dir,SrcDir}, {sftp_dst_dir,DstDir}, {src_file,SrcFile}, {sftp_size,N}
| Config].
@@ -333,52 +333,64 @@ find_time(accept_to_hello, L) ->
[T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) ->
C#call.t_call
end,
- fun(C=#call{mfa = {ssh_connection_handler,hello,_},
- args = [socket_control|_]}) ->
- C#call.t_return
- end
+ ?LINE,
+ fun(C=#call{mfa = {ssh_connection_handler,handle_event,4},
+ args = [_, {version_exchange,_}, {hello,_}, _]}) ->
+ C#call.t_call
+ end,
+ ?LINE
], L, []),
{accept_to_hello, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(kex, L) ->
- [T0,T1] = find([fun(C=#call{mfa = {ssh_connection_handler,hello,_},
- args = [socket_control|_]}) ->
+ [T0,T1] = find([fun(C=#call{mfa = {ssh_connection_handler,handle_event,4},
+ args = [_, {version_exchange,_}, {hello,_}, _]}) ->
C#call.t_call
end,
- ?send(#ssh_msg_newkeys{})
+ ?LINE,
+ ?send(#ssh_msg_newkeys{}),
+ ?LINE
], L, []),
{kex, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(kex_to_auth, L) ->
[T0,T1] = find([?send(#ssh_msg_newkeys{}),
- ?recv(#ssh_msg_userauth_request{})
+ ?LINE,
+ ?recv(#ssh_msg_userauth_request{}),
+ ?LINE
], L, []),
{kex_to_auth, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(auth, L) ->
[T0,T1] = find([?recv(#ssh_msg_userauth_request{}),
- ?send(#ssh_msg_userauth_success{})
+ ?LINE,
+ ?send(#ssh_msg_userauth_success{}),
+ ?LINE
], L, []),
{auth, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(to_prompt, L) ->
[T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) ->
C#call.t_call
end,
- ?recv(#ssh_msg_channel_request{request_type="env"})
+ ?LINE,
+ ?recv(#ssh_msg_channel_request{request_type="env"}),
+ ?LINE
], L, []),
{to_prompt, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(channel_open_close, L) ->
[T0,T1] = find([?recv(#ssh_msg_channel_request{request_type="subsystem"}),
- ?send(#ssh_msg_channel_close{})
+ ?LINE,
+ ?send(#ssh_msg_channel_close{}),
+ ?LINE
], L, []),
{channel_open_close, now2micro_sec(now_diff(T1,T0)), microsec}.
-find([F|Fs], [C|Cs], Acc) when is_function(F,1) ->
+find([F,Id|Fs], [C|Cs], Acc) when is_function(F,1) ->
try
F(C)
of
T -> find(Fs, Cs, [T|Acc])
catch
- _:_ -> find([F|Fs], Cs, Acc)
+ _:_ -> find([F,Id|Fs], Cs, Acc)
end;
find([], _, Acc) ->
lists:reverse(Acc).
@@ -444,7 +456,7 @@ erlang_trace() ->
0 = erlang:trace(new, true, [call,timestamp,{tracer,TracerPid}]),
[init_trace(MFA, tp(MFA))
|| MFA <- [{ssh_acceptor,handle_connection,5},
- {ssh_connection_handler,hello,2},
+%% {ssh_connection_handler,hello,2},
{ssh_message,encode,1},
{ssh_message,decode,1},
{ssh_transport,select_algorithm,3},
@@ -454,6 +466,10 @@ erlang_trace() ->
{ssh_message,decode,1},
{public_key,dh_gex_group,4} % To find dh_gex group size
]],
+ init_trace({ssh_connection_handler,handle_event,4},
+ [{['_', {version_exchange,'_'}, {hello,'_'}, '_'],
+ [],
+ [return_trace]}]),
{ok, TracerPid}.
tp({_M,_F,Arity}) ->
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index a5f424f863..0f757a0322 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -23,6 +23,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("ssh/src/ssh_connect.hrl").
+-include("ssh_test_lib.hrl").
-compile(export_all).
@@ -655,15 +656,21 @@ max_channels_option(Config) when is_list(Config) ->
{user_interaction, true},
{user_dir, UserDir}]),
+ %% Allocate a number of ChannelId:s to play with. (This operation is not
+ %% counted by the max_channel option).
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
{ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
{ok, ChannelId2} = ssh_connection:session_channel(ConnectionRef, infinity),
{ok, ChannelId3} = ssh_connection:session_channel(ConnectionRef, infinity),
{ok, ChannelId4} = ssh_connection:session_channel(ConnectionRef, infinity),
{ok, ChannelId5} = ssh_connection:session_channel(ConnectionRef, infinity),
- {ok, _ChannelId6} = ssh_connection:session_channel(ConnectionRef, infinity),
+ {ok, ChannelId6} = ssh_connection:session_channel(ConnectionRef, infinity),
+ {ok, _ChannelId7} = ssh_connection:session_channel(ConnectionRef, infinity),
- %%%---- shell
+ %% Now start to open the channels (this is counted my max_channels) to check that
+ %% it gives a failure at right place
+
+ %%%---- Channel 1(3): shell
ok = ssh_connection:shell(ConnectionRef,ChannelId0),
receive
{ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"Eshell",_/binary>>}} ->
@@ -672,10 +679,10 @@ max_channels_option(Config) when is_list(Config) ->
ct:fail("CLI Timeout")
end,
- %%%---- subsystem "echo_n"
+ %%%---- Channel 2(3): subsystem "echo_n"
success = ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", infinity),
- %%%---- exec #1
+ %%%---- Channel 3(3): exec. This closes itself.
success = ssh_connection:exec(ConnectionRef, ChannelId2, "testing1.\n", infinity),
receive
{ssh_cm, ConnectionRef, {data, ChannelId2, 0, <<"testing1",_/binary>>}} ->
@@ -684,13 +691,13 @@ max_channels_option(Config) when is_list(Config) ->
ct:fail("Exec #1 Timeout")
end,
- %%%---- ptty
- success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId3, []),
+ %%%---- Channel 3(3): subsystem "echo_n" (Note that ChannelId2 should be closed now)
+ ?wait_match(success, ssh_connection:subsystem(ConnectionRef, ChannelId3, "echo_n", infinity)),
- %%%---- exec #2
+ %%%---- Channel 4(3) !: exec This should fail
failure = ssh_connection:exec(ConnectionRef, ChannelId4, "testing2.\n", infinity),
- %%%---- close the shell
+ %%%---- close the shell (Frees one channel)
ok = ssh_connection:send(ConnectionRef, ChannelId0, "exit().\n", 5000),
%%%---- wait for the subsystem to terminate
@@ -703,14 +710,11 @@ max_channels_option(Config) when is_list(Config) ->
ct:fail("exit Timeout",[])
end,
- %%%---- exec #3
- success = ssh_connection:exec(ConnectionRef, ChannelId5, "testing3.\n", infinity),
- receive
- {ssh_cm, ConnectionRef, {data, ChannelId5, 0, <<"testing3",_/binary>>}} ->
- ok
- after 5000 ->
- ct:fail("Exec #3 Timeout")
- end,
+ %%---- Try that we can open one channel instead of the closed one
+ ?wait_match(success, ssh_connection:subsystem(ConnectionRef, ChannelId5, "echo_n", infinity)),
+
+ %%---- But not a fourth one...
+ failure = ssh_connection:subsystem(ConnectionRef, ChannelId6, "echo_n", infinity),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 1d14a16065..4ca6a473fa 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -51,9 +51,7 @@
ssh_connect_arg4_timeout/1,
ssh_connect_negtimeout_parallel/1,
ssh_connect_negtimeout_sequential/1,
- ssh_connect_nonegtimeout_connected_parallel/0,
ssh_connect_nonegtimeout_connected_parallel/1,
- ssh_connect_nonegtimeout_connected_sequential/0,
ssh_connect_nonegtimeout_connected_sequential/1,
ssh_connect_timeout/1, connect/4,
ssh_daemon_minimal_remote_max_packet_size_option/1,
@@ -82,7 +80,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap,{seconds,40}}].
+ {timetrap,{seconds,30}}].
all() ->
[connectfun_disconnectfun_server,
@@ -493,7 +491,7 @@ ssh_msg_debug_fun_option_client(Config) ->
{user_interaction, false},
{ssh_msg_debug_fun,DbgFun}]),
%% Beware, implementation knowledge:
- gen_fsm:send_all_state_event(ConnectionRef,{ssh_msg_debug,false,<<"Hello">>,<<>>}),
+ gen_statem:cast(ConnectionRef,{ssh_msg_debug,false,<<"Hello">>,<<>>}),
receive
{msg_dbg,X={ConnectionRef,false,<<"Hello">>,<<>>}} ->
ct:log("Got expected dbg msg ~p",[X]),
@@ -606,7 +604,7 @@ ssh_msg_debug_fun_option_server(Config) ->
receive
{connection_pid,Server} ->
%% Beware, implementation knowledge:
- gen_fsm:send_all_state_event(Server,{ssh_msg_debug,false,<<"Hello">>,<<>>}),
+ gen_statem:cast(Server,{ssh_msg_debug,false,<<"Hello">>,<<>>}),
receive
{msg_dbg,X={_,false,<<"Hello">>,<<>>}} ->
ct:log("Got expected dbg msg ~p",[X]),
@@ -982,16 +980,10 @@ ssh_connect_negtimeout(Config, Parallel) ->
%%--------------------------------------------------------------------
%%% Test that ssh connection does not timeout if the connection is established (parallel)
-
-ssh_connect_nonegtimeout_connected_parallel() -> [{timetrap,{seconds,90}}].
-
ssh_connect_nonegtimeout_connected_parallel(Config) ->
ssh_connect_nonegtimeout_connected(Config, true).
%%% Test that ssh connection does not timeout if the connection is established (non-parallel)
-
-ssh_connect_nonegtimeout_connected_sequential() -> [{timetrap,{seconds,90}}].
-
ssh_connect_nonegtimeout_connected_sequential(Config) ->
ssh_connect_nonegtimeout_connected(Config, false).
@@ -1000,7 +992,7 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
UserDir = ?config(priv_dir, Config),
- NegTimeOut = 20000, % ms
+ NegTimeOut = 2000, % ms
ct:log("Parallel: ~p",[Parallel]),
{_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir},
@@ -1131,21 +1123,7 @@ max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) ->
%% This is expected
%% Now stop one connection and try to open one more
ok = ssh:close(hd(Connections)),
- receive after 250 -> ok end, % sleep so the supervisor has time to count down. Not nice...
- try Connect(Host,Port)
- of
- _ConnectionRef1 ->
- %% Step 3 ok: could set up one more connection after killing one
- %% Thats good.
- ssh:stop_daemon(Pid),
- ok
- catch
- error:{badmatch,{error,"Connection closed"}} ->
- %% Bad indeed. Could not set up one more connection even after killing
- %% one existing. Very bad.
- ssh:stop_daemon(Pid),
- {fail,"Does not decrease # active sessions"}
- end
+ try_to_connect(Connect, Host, Port, Pid)
end
catch
error:{badmatch,{error,"Connection closed"}} ->
@@ -1153,6 +1131,35 @@ max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) ->
{fail,"Too few connections accepted"}
end.
+
+try_to_connect(Connect, Host, Port, Pid) ->
+ {ok,Tref} = timer:send_after(3000, timeout_no_connection), % give the supervisors some time...
+ try_to_connect(Connect, Host, Port, Pid, Tref, 1). % will take max 3300 ms after 11 tries
+
+try_to_connect(Connect, Host, Port, Pid, Tref, N) ->
+ try Connect(Host,Port)
+ of
+ _ConnectionRef1 ->
+ %% Step 3 ok: could set up one more connection after killing one
+ %% Thats good.
+ timer:cancel(Tref),
+ ssh:stop_daemon(Pid),
+ receive % flush.
+ timeout_no_connection -> ok
+ after 0 -> ok
+ end
+ catch
+ error:{badmatch,{error,"Connection closed"}} ->
+ %% Could not set up one more connection. Try again until timeout.
+ receive
+ timeout_no_connection ->
+ ssh:stop_daemon(Pid),
+ {fail,"Does not decrease # active sessions"}
+ after N*50 -> % retry after this time
+ try_to_connect(Connect, Host, Port, Pid, Tref, N+1)
+ end
+ end.
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl
index 90132becbd..f1a909cbd0 100644
--- a/lib/ssh/test/ssh_renegotiate_SUITE.erl
+++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl
@@ -33,7 +33,6 @@
suite() -> [{ct_hooks,[ts_install_cth]},
{timetrap,{seconds,40}}].
-
all() -> [{group,default_algs},
{group,aes_gcm}
].
@@ -238,7 +237,7 @@ renegotiate2(Config) ->
%% get_kex_init - helper function to get key_exchange_init_msg
get_kex_init(Conn) ->
%% First, validate the key exchange is complete (StateName == connected)
- {connected,S} = sys:get_state(Conn),
+ {{connected,_},S} = sys:get_state(Conn),
%% Next, walk through the elements of the #state record looking
%% for the #ssh_msg_kexinit record. This method is robust against
%% changes to either record. The KEXINIT message contains a cookie
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index c4bb02841b..f6d7be41d6 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -38,7 +38,6 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{seconds,40}}].
-
all() ->
[{group, not_unicode},
{group, unicode}
@@ -301,9 +300,9 @@ end_per_testcase(_, Config) ->
end_per_testcase(Config) ->
{Sftp, Connection} = ?config(sftp, Config),
- ssh_sftp:stop_channel(Sftp),
+ ok = ssh_sftp:stop_channel(Sftp),
catch ssh_sftp:stop_channel(?config(channel_pid2, Config)),
- ssh:close(Connection).
+ ok = ssh:close(Connection).
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
@@ -365,7 +364,7 @@ write_file(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
Data = list_to_binary("Hej hopp!"),
- ssh_sftp:write_file(Sftp, FileName, [Data]),
+ ok = ssh_sftp:write_file(Sftp, FileName, [Data]),
{ok, Data} = file:read_file(FileName).
%%--------------------------------------------------------------------
@@ -378,7 +377,7 @@ write_file_iolist(Config) when is_list(Config) ->
Data = list_to_binary("Hej hopp!"),
lists:foreach(
fun(D) ->
- ssh_sftp:write_file(Sftp, FileName, [D]),
+ ok = ssh_sftp:write_file(Sftp, FileName, [D]),
Expected = if is_binary(D) -> D;
is_list(D) -> list_to_binary(D)
end,
@@ -397,7 +396,7 @@ write_big_file(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
Data = list_to_binary(lists:duplicate(750000,"a")),
- ssh_sftp:write_file(Sftp, FileName, [Data]),
+ ok = ssh_sftp:write_file(Sftp, FileName, [Data]),
{ok, Data} = file:read_file(FileName).
%%--------------------------------------------------------------------
@@ -409,7 +408,7 @@ sftp_read_big_file(Config) when is_list(Config) ->
Data = list_to_binary(lists:duplicate(750000,"a")),
ct:log("Data size to write is ~p bytes",[size(Data)]),
- ssh_sftp:write_file(Sftp, FileName, [Data]),
+ ok = ssh_sftp:write_file(Sftp, FileName, [Data]),
{ok, Data} = ssh_sftp:read_file(Sftp, FileName).
%%--------------------------------------------------------------------
@@ -425,7 +424,7 @@ remove_file(Config) when is_list(Config) ->
ok = ssh_sftp:delete(Sftp, FileName),
{ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir),
false = lists:member(filename:basename(FileName), NewFiles),
- {error, _} = ssh_sftp:delete(Sftp, FileName).
+ {error, no_such_file} = ssh_sftp:delete(Sftp, FileName).
%%--------------------------------------------------------------------
rename_file() ->
[{doc, "Test API function rename_file/2"}].
@@ -500,7 +499,7 @@ set_attributes(Config) when is_list(Config) ->
io:put_chars(Fd,"foo"),
ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#400}),
{error, eacces} = file:write_file(FileName, "hello again"),
- ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}),
+ ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}),
ok = file:write_file(FileName, "hello again").
%%--------------------------------------------------------------------
@@ -549,7 +548,7 @@ position(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
Data = list_to_binary("1234567890"),
- ssh_sftp:write_file(Sftp, FileName, [Data]),
+ ok = ssh_sftp:write_file(Sftp, FileName, [Data]),
{ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),
{ok, 3} = ssh_sftp:position(Sftp, Handle, {bof, 3}),
@@ -577,7 +576,7 @@ pos_read(Config) when is_list(Config) ->
FileName = ?config(testfile, Config),
{Sftp, _} = ?config(sftp, Config),
Data = list_to_binary("Hej hopp!"),
- ssh_sftp:write_file(Sftp, FileName, [Data]),
+ ok = ssh_sftp:write_file(Sftp, FileName, [Data]),
{ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),
{async, Ref} = ssh_sftp:apread(Sftp, Handle, {bof, 5}, 4),
@@ -607,7 +606,7 @@ pos_write(Config) when is_list(Config) ->
{ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]),
Data = list_to_binary("Bye,"),
- ssh_sftp:write_file(Sftp, FileName, [Data]),
+ ok = ssh_sftp:write_file(Sftp, FileName, [Data]),
NewData = list_to_binary(" see you tomorrow"),
{async, Ref} = ssh_sftp:apwrite(Sftp, Handle, {bof, 4}, NewData),
@@ -869,7 +868,7 @@ aes_cbc256_crypto_tar(Config) ->
{"d1",fn("d1",Config)} % Dir
]),
Key = <<"This is a 256 bit key. Boring...">>,
- Ivec0 = crypto:rand_bytes(16),
+ Ivec0 = crypto:strong_rand_bytes(16),
DataSize = 1024, % data_size rem 16 = 0 for aes_cbc
Cinitw = fun() -> {ok, Ivec0, DataSize} end,
@@ -914,7 +913,7 @@ aes_ctr_stream_crypto_tar(Config) ->
{"d1",fn("d1",Config)} % Dir
]),
Key = <<"This is a 256 bit key. Boring...">>,
- Ivec0 = crypto:rand_bytes(16),
+ Ivec0 = crypto:strong_rand_bytes(16),
Cinitw = Cinitr = fun() -> {ok, crypto:stream_init(aes_ctr,Key,Ivec0)} end,
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index fb1a9687af..9385bd127d 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -105,7 +105,6 @@ init_per_testcase(TestCase, Config) ->
ClientUserDir = filename:join(PrivDir, nopubkey),
SystemDir = filename:join(?config(priv_dir, Config), system),
- Port = ssh_test_lib:inet_port(node()),
Options = [{system_dir, SystemDir},
{user_dir, PrivDir},
{user_passwords,[{?USER, ?PASSWD}]},
@@ -113,11 +112,13 @@ init_per_testcase(TestCase, Config) ->
{ok, Sftpd} = case TestCase of
ver6_basic ->
SubSystems = [ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}])],
- ssh:daemon(Port, [{subsystems, SubSystems}|Options]);
+ ssh:daemon(0, [{subsystems, SubSystems}|Options]);
_ ->
SubSystems = [ssh_sftpd:subsystem_spec([])],
- ssh:daemon(Port, [{subsystems, SubSystems}|Options])
+ ssh:daemon(0, [{subsystems, SubSystems}|Options])
end,
+ {ok,Dinf} = ssh:daemon_info(Sftpd),
+ Port = proplists:get_value(port, Dinf),
Cm = ssh_test_lib:connect(Port,
[{user_dir, ClientUserDir},
diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
index 09bef87148..355ce6a8f5 100644
--- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
@@ -39,7 +39,6 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{seconds,40}}].
-
all() ->
[close_file,
quit,
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index f800ea806d..f8929b30ff 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -22,21 +22,23 @@
-module(ssh_sup_SUITE).
-include_lib("common_test/include/ct.hrl").
-include_lib("ssh/src/ssh.hrl").
+-include("ssh_test_lib.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
--define(WAIT_FOR_SHUTDOWN, 500).
-define(USER, "Alladin").
-define(PASSWD, "Sesame").
+-define(WAIT_FOR_SHUTDOWN, 500).
+
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap,{seconds,40}}].
+ {timetrap,{seconds,100}}].
all() ->
[default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile].
@@ -92,8 +94,8 @@ default_tree(Config) when is_list(Config) ->
lists:keysearch(sshc_sup, 1, TopSupChildren),
{value, {sshd_sup, _,supervisor,[sshd_sup]}} =
lists:keysearch(sshd_sup, 1, TopSupChildren),
- [] = supervisor:which_children(sshc_sup),
- [] = supervisor:which_children(sshd_sup).
+ ?wait_match([], supervisor:which_children(sshc_sup)),
+ ?wait_match([], supervisor:which_children(sshd_sup)).
sshc_subtree() ->
[{doc, "Make sure the sshc subtree is correct"}].
@@ -101,24 +103,27 @@ sshc_subtree(Config) when is_list(Config) ->
{_Pid, Host, Port} = ?config(server, Config),
UserDir = ?config(userdir, Config),
- [] = supervisor:which_children(sshc_sup),
+ ?wait_match([], supervisor:which_children(sshc_sup)),
+
{ok, Pid1} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_interaction, false},
{user, ?USER}, {password, ?PASSWD},{user_dir, UserDir}]),
- [{_, _,supervisor,[ssh_connection_handler]}] =
- supervisor:which_children(sshc_sup),
+
+ ?wait_match([{_, _,worker,[ssh_connection_handler]}],
+ supervisor:which_children(sshc_sup)),
+
{ok, Pid2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_interaction, false},
{user, ?USER}, {password, ?PASSWD}, {user_dir, UserDir}]),
- [{_,_,supervisor,[ssh_connection_handler]},
- {_,_,supervisor,[ssh_connection_handler]}] =
- supervisor:which_children(sshc_sup),
+ ?wait_match([{_,_,worker,[ssh_connection_handler]},
+ {_,_,worker,[ssh_connection_handler]}],
+ supervisor:which_children(sshc_sup)),
+
ssh:close(Pid1),
- [{_,_,supervisor,[ssh_connection_handler]}] =
- supervisor:which_children(sshc_sup),
+ ?wait_match([{_,_,worker,[ssh_connection_handler]}],
+ supervisor:which_children(sshc_sup)),
ssh:close(Pid2),
- ct:sleep(?WAIT_FOR_SHUTDOWN),
- [] = supervisor:which_children(sshc_sup).
+ ?wait_match([], supervisor:which_children(sshc_sup)).
sshd_subtree() ->
[{doc, "Make sure the sshd subtree is correct"}].
@@ -130,14 +135,16 @@ sshd_subtree(Config) when is_list(Config) ->
{failfun, fun ssh_test_lib:failfun/2},
{user_passwords,
[{?USER, ?PASSWD}]}]),
- [{{server,ssh_system_sup, HostIP, Port, ?DEFAULT_PROFILE},
- Daemon, supervisor,
- [ssh_system_sup]}] =
- supervisor:which_children(sshd_sup),
+
+ ?wait_match([{{server,ssh_system_sup, HostIP, Port, ?DEFAULT_PROFILE},
+ Daemon, supervisor,
+ [ssh_system_sup]}],
+ supervisor:which_children(sshd_sup),
+ Daemon),
check_sshd_system_tree(Daemon, Config),
ssh:stop_daemon(HostIP, Port),
ct:sleep(?WAIT_FOR_SHUTDOWN),
- [] = supervisor:which_children(sshd_sup).
+ ?wait_match([], supervisor:which_children(sshd_sup)).
sshd_subtree_profile() ->
[{doc, "Make sure the sshd subtree using profile option is correct"}].
@@ -152,14 +159,15 @@ sshd_subtree_profile(Config) when is_list(Config) ->
{user_passwords,
[{?USER, ?PASSWD}]},
{profile, Profile}]),
- [{{server,ssh_system_sup, HostIP,Port,Profile},
- Daemon, supervisor,
- [ssh_system_sup]}] =
- supervisor:which_children(sshd_sup),
+ ?wait_match([{{server,ssh_system_sup, HostIP,Port,Profile},
+ Daemon, supervisor,
+ [ssh_system_sup]}],
+ supervisor:which_children(sshd_sup),
+ Daemon),
check_sshd_system_tree(Daemon, Config),
ssh:stop_daemon(HostIP, Port, Profile),
ct:sleep(?WAIT_FOR_SHUTDOWN),
- [] = supervisor:which_children(sshd_sup).
+ ?wait_match([], supervisor:which_children(sshd_sup)).
check_sshd_system_tree(Daemon, Config) ->
@@ -170,28 +178,31 @@ check_sshd_system_tree(Daemon, Config) ->
{user_interaction, false},
{user, ?USER}, {password, ?PASSWD},{user_dir, UserDir}]),
- [{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
- {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}]
- = supervisor:which_children(Daemon),
+ ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
+ {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
+ supervisor:which_children(Daemon),
+ [SubSysSup,AccSup]),
- [{{server,ssh_connection_sup, _,_},
- ConnectionSup, supervisor,
- [ssh_connection_sup]},
- {{server,ssh_channel_sup,_ ,_},
- ChannelSup,supervisor,
- [ssh_channel_sup]}] = supervisor:which_children(SubSysSup),
+ ?wait_match([{{server,ssh_connection_sup, _,_},
+ ConnectionSup, supervisor,
+ [ssh_connection_sup]},
+ {{server,ssh_channel_sup,_ ,_},
+ ChannelSup,supervisor,
+ [ssh_channel_sup]}],
+ supervisor:which_children(SubSysSup),
+ [ConnectionSup,ChannelSup]),
- [{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}] =
- supervisor:which_children(AccSup),
+ ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
+ supervisor:which_children(AccSup)),
- [{_, _, worker,[ssh_connection_handler]}] =
- supervisor:which_children(ConnectionSup),
+ ?wait_match([{_, _, worker,[ssh_connection_handler]}],
+ supervisor:which_children(ConnectionSup)),
- [] = supervisor:which_children(ChannelSup),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
ssh_sftp:start_channel(Client),
- [{_, _,worker,[ssh_channel]}] =
- supervisor:which_children(ChannelSup),
+ ?wait_match([{_, _,worker,[ssh_channel]}],
+ supervisor:which_children(ChannelSup)),
ssh:close(Client).
diff --git a/lib/ssh/test/ssh_test_cli.erl b/lib/ssh/test/ssh_test_cli.erl
index 697ddb730d..f96b9967d2 100644
--- a/lib/ssh/test/ssh_test_cli.erl
+++ b/lib/ssh/test/ssh_test_cli.erl
@@ -75,10 +75,11 @@ terminate(_Why, _S) ->
run_portprog(User, cli, TmpDir) ->
Pty_bin = os:find_executable("cat"),
- open_port({spawn_executable, Pty_bin},
- [stream, {cd, TmpDir}, {env, [{"USER", User}]},
- {args, []}, binary,
- exit_status, use_stdio, stderr_to_stdout]).
+ ssh_test_lib:open_port({spawn_executable, Pty_bin},
+ [stream,
+ {cd, TmpDir},
+ {env, [{"USER", User}]},
+ {args, []}]).
get_ssh_user(Ref) ->
[{user, User}] = ssh:connection_info(Ref, [user]),
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index abbd4857c9..c6541461a1 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -32,15 +32,8 @@
-define(TIMEOUT, 50000).
-connect(Options) ->
- connect(hostname(), inet_port(), Options).
-
connect(Port, Options) when is_integer(Port) ->
- connect(hostname(), Port, Options);
-connect(any, Options) ->
- connect(hostname(), inet_port(), Options);
-connect(Host, Options) ->
- connect(Host, inet_port(), Options).
+ connect(hostname(), Port, Options).
connect(any, Port, Options) ->
connect(hostname(), Port, Options);
@@ -49,23 +42,33 @@ connect(Host, Port, Options) ->
ConnectionRef.
daemon(Options) ->
- daemon(any, inet_port(), Options).
+ daemon(any, 0, Options).
daemon(Port, Options) when is_integer(Port) ->
daemon(any, Port, Options);
daemon(Host, Options) ->
- daemon(Host, inet_port(), Options).
+ daemon(Host, 0, Options).
+
daemon(Host, Port, Options) ->
+ ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
case ssh:daemon(Host, Port, Options) of
{ok, Pid} when Host == any ->
- {Pid, hostname(), Port};
+ ct:log("ssh:daemon ok (1)",[]),
+ {Pid, hostname(), daemon_port(Port,Pid)};
{ok, Pid} ->
- {Pid, Host, Port};
+ ct:log("ssh:daemon ok (2)",[]),
+ {Pid, Host, daemon_port(Port,Pid)};
Error ->
+ ct:log("ssh:daemon error ~p",[Error]),
Error
end.
+daemon_port(0, Pid) -> {ok,Dinf} = ssh:daemon_info(Pid),
+ proplists:get_value(port, Dinf);
+daemon_port(Port, _) -> Port.
+
+
std_daemon(Config, ExtraOpts) ->
PrivDir = ?config(priv_dir, Config),
@@ -100,7 +103,7 @@ std_simple_sftp(Host, Port, Config, Opts) ->
DataFile = filename:join(UserDir, "test.data"),
ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, Opts),
{ok, ChannelRef} = ssh_sftp:start_channel(ConnectionRef),
- Data = crypto:rand_bytes(proplists:get_value(std_simple_sftp_size,Config,10)),
+ Data = crypto:strong_rand_bytes(proplists:get_value(std_simple_sftp_size,Config,10)),
ok = ssh_sftp:write_file(ChannelRef, DataFile, Data),
{ok,ReadData} = file:read_file(DataFile),
ok = ssh:close(ConnectionRef),
@@ -201,6 +204,35 @@ reply(TestCase, Result) ->
%%ct:log("reply ~p sending ~p ! ~p",[self(), TestCase, Result]),
TestCase ! Result.
+
+
+rcv_expected(Expect, SshPort, Timeout) ->
+ receive
+ {SshPort, Expect} ->
+ ct:log("Got expected ~p from ~p",[Expect,SshPort]),
+ catch port_close(SshPort),
+ rcv_lingering(50);
+ Other ->
+ ct:log("Got UNEXPECTED ~p~nExpect ~p",[Other, {SshPort,Expect}]),
+ rcv_expected(Expect, SshPort, Timeout)
+
+ after Timeout ->
+ catch port_close(SshPort),
+ ct:fail("Did not receive answer")
+ end.
+
+rcv_lingering(Timeout) ->
+ receive
+ Msg ->
+ ct:log("Got LINGERING ~p",[Msg]),
+ rcv_lingering(Timeout)
+
+ after Timeout ->
+ ct:log("No more lingering messages",[]),
+ ok
+ end.
+
+
receive_exec_result(Msg) ->
ct:log("Expect data! ~p", [Msg]),
receive
@@ -354,7 +386,7 @@ setup_rsa_pass_pharse(DataDir, UserDir, Phrase) ->
setup_pass_pharse(KeyBin, OutFile, Phrase) ->
[{KeyType, _,_} = Entry0] = public_key:pem_decode(KeyBin),
Key = public_key:pem_entry_decode(Entry0),
- Salt = crypto:rand_bytes(8),
+ Salt = crypto:strong_rand_bytes(8),
Entry = public_key:pem_entry_encode(KeyType, Key,
{{"DES-CBC", Salt}, Phrase}),
Pem = public_key:pem_encode([Entry]),
@@ -470,8 +502,9 @@ openssh_supports(ClientOrServer, Tag, Alg) when ClientOrServer == sshc ;
%% Check if we have a "newer" ssh client that supports these test cases
ssh_client_supports_Q() ->
- ErlPort = open_port({spawn, "ssh -Q cipher"}, [exit_status, stderr_to_stdout]),
- 0 == check_ssh_client_support2(ErlPort).
+ 0 == check_ssh_client_support2(
+ ?MODULE:open_port({spawn, "ssh -Q cipher"})
+ ).
check_ssh_client_support2(P) ->
receive
@@ -690,3 +723,16 @@ has_inet6_address() ->
catch
throw:6 -> true
end.
+
+%%%----------------------------------------------------------------
+open_port(Arg1) ->
+ ?MODULE:open_port(Arg1, []).
+
+open_port(Arg1, ExtraOpts) ->
+ erlang:open_port(Arg1,
+ [binary,
+ stderr_to_stdout,
+ exit_status,
+ use_stdio,
+ overlapped_io, hide %only affects windows
+ | ExtraOpts]).
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
new file mode 100644
index 0000000000..7cb7edeaa8
--- /dev/null
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -0,0 +1,27 @@
+%%-------------------------------------------------------------------------
+%% Help macro
+%%-------------------------------------------------------------------------
+-define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+ Bind =
+ (fun() ->
+ F = fun(N, F1) ->
+ case FunctionCall of
+ Pattern -> Bind;
+ _ when N>0 ->
+ ct:pal("Must sleep ~p ms at ~p:~p",[Timeout,?MODULE,?LINE]),
+ timer:sleep(Timeout),
+ F1(N-1, F1);
+ Other ->
+ ct:fail("Unexpected ~p:~p ~p",[?MODULE,?LINE,Other])
+ end
+ end,
+ F(Ntries, F)
+ end)()
+ ).
+
+-define(wait_match(Pattern, FunctionCall, Timeout, Ntries), ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
+
+-define(wait_match(Pattern, FunctionCall, Bind), ?wait_match(Pattern, FunctionCall, Bind, 500, 10) ).
+
+-define(wait_match(Pattern, FunctionCall), ?wait_match(Pattern, FunctionCall, ok) ).
+
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 5b65edc32f..2be75fd7f3 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -34,7 +34,7 @@
%%--------------------------------------------------------------------
suite() ->
- [{timetrap,{seconds,40}}].
+ [{timetrap,{seconds,20}}].
all() ->
case os:find_executable("ssh") of
@@ -50,13 +50,15 @@ groups() ->
[{erlang_client, [], [erlang_shell_client_openssh_server,
erlang_client_openssh_server_exec_compressed,
erlang_client_openssh_server_setenv,
- erlang_client_openssh_server_publickey_rsa,
erlang_client_openssh_server_publickey_dsa,
+ erlang_client_openssh_server_publickey_rsa,
erlang_client_openssh_server_password,
erlang_client_openssh_server_kexs,
erlang_client_openssh_server_nonexistent_subsystem
]},
- {erlang_server, [], [erlang_server_openssh_client_public_key_dsa]}
+ {erlang_server, [], [erlang_server_openssh_client_public_key_dsa,
+ erlang_server_openssh_client_public_key_rsa
+ ]}
].
init_per_suite(Config) ->
@@ -74,6 +76,7 @@ init_per_group(erlang_server, Config) ->
DataDir = ?config(data_dir, Config),
UserDir = ?config(priv_dir, Config),
ssh_test_lib:setup_dsa_known_host(DataDir, UserDir),
+ ssh_test_lib:setup_rsa_known_host(DataDir, UserDir),
Config;
init_per_group(erlang_client, Config) ->
CommonAlgs = ssh_test_lib:algo_intersection(
@@ -86,6 +89,7 @@ init_per_group(_, Config) ->
end_per_group(erlang_server, Config) ->
UserDir = ?config(priv_dir, Config),
ssh_test_lib:clean_dsa(UserDir),
+ ssh_test_lib:clean_rsa(UserDir),
Config;
end_per_group(_, Config) ->
Config.
@@ -93,6 +97,8 @@ end_per_group(_, Config) ->
init_per_testcase(erlang_server_openssh_client_public_key_dsa, Config) ->
chk_key(sshc, 'ssh-dss', ".ssh/id_dsa", Config);
+init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) ->
+ chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config);
init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) ->
chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config);
init_per_testcase(_TestCase, Config) ->
@@ -347,14 +353,24 @@ erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
erlang_server_openssh_client_public_key_dsa() ->
- [{doc, "Validate using dsa publickey."}].
+ [{timetrap, {seconds,(?TIMEOUT div 1000)+10}},
+ {doc, "Validate using dsa publickey."}].
erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
+ erlang_server_openssh_client_public_key_X(Config, ssh_dsa).
+
+erlang_server_openssh_client_public_key_rsa() ->
+ [{timetrap, {seconds,(?TIMEOUT div 1000)+10}},
+ {doc, "Validate using rsa publickey."}].
+erlang_server_openssh_client_public_key_rsa(Config) when is_list(Config) ->
+ erlang_server_openssh_client_public_key_X(Config, ssh_rsa).
+
+
+erlang_server_openssh_client_public_key_X(Config, PubKeyAlg) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
KnownHosts = filename:join(PrivDir, "known_hosts"),
-
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
- {public_key_alg, ssh_dsa},
+ {public_key_alg, PubKeyAlg},
{failfun, fun ssh_test_lib:failfun/2}]),
ct:sleep(500),
@@ -362,18 +378,8 @@ erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
Cmd = "ssh -p " ++ integer_to_list(Port) ++
" -o UserKnownHostsFile=" ++ KnownHosts ++
" " ++ Host ++ " 1+1.",
- SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]),
-
- receive
- {SshPort,{data, <<"2\n">>}} ->
- ok
- after ?TIMEOUT ->
- receive
- X -> ct:fail("Received: ~p",[X])
- after 0 ->
- ct:fail("Did not receive answer")
- end
- end,
+ OpenSsh = ssh_test_lib:open_port({spawn, Cmd}),
+ ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT),
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key
new file mode 100644
index 0000000000..79968bdd7d
--- /dev/null
+++ b/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key
@@ -0,0 +1,16 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337
+zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB
+6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB
+AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW
+NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++
+udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW
+WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt
+n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5
+sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY
++SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt
+64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB
+m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT
+tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR
+-----END RSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..75d2025c71
--- /dev/null
+++ b/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key.pub
@@ -0,0 +1,5 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8
+semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW
+RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q==
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
index 4269529ae8..e34071af99 100644
--- a/lib/ssh/test/ssh_trpt_test_lib.erl
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -294,12 +294,11 @@ instantiate(X, _S) ->
%%%================================================================
%%%
init_ssh(Role, Socket, Options0) ->
- Options = [{user_interaction,false}
+ Options = [{user_interaction, false},
+ {vsn, {2,0}},
+ {id_string, "ErlangTestLib"}
| Options0],
- ssh_connection_handler:init_ssh(Role,
- {2,0},
- lists:concat(["SSH-2.0-ErlangTestLib ",Role]),
- Options, Socket).
+ ssh_connection_handler:init_ssh_record(Role, Socket, Options).
mangle_opts(Options) ->
SysOpts = [{reuseaddr, true},
diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl
index 5c7ec17dac..06bef2455e 100644
--- a/lib/ssh/test/ssh_upgrade_SUITE.erl
+++ b/lib/ssh/test/ssh_upgrade_SUITE.erl
@@ -146,7 +146,8 @@ setup_server_client(#state{config=Config} = State) ->
SFTP = ssh_sftpd:subsystem_spec([{root,FtpRootDir},{cwd,FtpRootDir}]),
- {Server,Host,Port} = ssh_test_lib:daemon([{system_dir,DataDir},
+ {Server,Host,Port} = ssh_test_lib:daemon(ssh_test_lib:inet_port(), % when lower rel is 18.x
+ [{system_dir,DataDir},
{user_passwords,[{"hej","hopp"}]},
{subsystems,[SFTP]}]),
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 41b42d454b..b165928877 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.2.2
+SSH_VSN = 4.3
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 7c55dd4c96..e9b523d9e1 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,53 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 7.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct cipher suites conversion and gaurd expression.
+ Caused problems with GCM cipher suites and client side
+ option to set signature_algorithms extention values.</p>
+ <p>
+ Own Id: OTP-13525</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 7.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Corrections to cipher suite handling using the 3 and 4
+ tuple format in addition to commit
+ 89d7e21cf4ae988c57c8ef047bfe85127875c70c</p>
+ <p>
+ Own Id: OTP-13511</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Make values for the TLS-1.2 signature_algorithms
+ extension configurable</p>
+ <p>
+ Own Id: OTP-13261</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 7.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index e490de7eeb..82d6faee42 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -21,7 +21,7 @@
%% Internal application API
--behaviour(gen_fsm).
+-behaviour(gen_statem).
-include("dtls_connection.hrl").
-include("dtls_handshake.hrl").
@@ -36,37 +36,38 @@
%% Internal application API
%% Setup
--export([start_fsm/8]).
+-export([start_fsm/8, start_link/7, init/1]).
%% State transition handling
--export([next_record/1, next_state/4%,
- %%next_state_connection/2
- ]).
+-export([next_record/1, next_event/3]).
%% Handshake handling
--export([%%renegotiate/1,
+-export([%%renegotiate/2,
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
+-export([%%send_alert/2, handle_own_alert/4, handle_close_alert/3,
+ handle_normal_shutdown/3 %%, close/5
+ %%alert_user/6, alert_user/9
]).
%% Data handling
-export([%%write_application_data/3,
- read_application_data/2%%,
-%% passive_receive/2, next_record_if_active/1
+ read_application_data/2,
+ %%passive_receive/2,
+ next_record_if_active/1 %%,
+ %%handle_common_event/4
]).
-%% Called by tls_connection_sup
--export([start_link/7]).
+%% gen_statem state functions
+-export([init/3, error/3, downgrade/3, %% Initiation and take down states
+ hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
+ connection/3]).
+%% gen_statem callbacks
+-export([terminate/3, code_change/4, format_status/2]).
-%% 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]).
+-define(GEN_STATEM_CB_MODE, state_functions).
%%====================================================================
%% Internal application API
@@ -141,75 +142,74 @@ send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
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]) ->
+init([Role, Host, Port, Socket, 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 = erlang:monotonic_time(),
- try ssl_config:init(SSLOpts0, Role) of
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, 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,
- crl_db = CRLDbInfo,
- session_cache = CacheHandle,
- private_key = Key,
- diffie_hellman_params = DHParams},
- gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State))
+ try
+ State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
+ gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, init, State)
catch
throw:Error ->
- gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0))
+ gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error,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) ->
+%% State functionsconnection/2
+%%--------------------------------------------------------------------
+
+init({call, From}, {start, Timeout},
+ #state{host = Host, port = Port, role = client,
+ ssl_options = SslOpts,
+ session = #session{own_certificate = Cert} = Session0,
+ transport_cb = Transport, socket = Socket,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb
+ } = State0) ->
+ Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From),
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
- Cache, CacheCb, Renegotiation, Cert),
+ Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
+ HelloVersion = dtls_record:lowest_protocol_version(SslOpts#ssl_options.versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
- encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
+ encode_handshake(Hello, HelloVersion, 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},
+ tls_handshake_history = Handshake,
+ start_or_recv_from = From,
+ timer = Timer},
{Record, State} = next_record(State1),
- next_state(hello, hello, Record, State);
-
-hello(Hello = #client_hello{client_version = ClientVersion},
- 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}) ->
+ next_event(hello, Record, State);
+init(Type, Event, State) ->
+ ssl_connection:init(Type, Event, State, ?MODULE).
+
+error({call, From}, {start, _Timeout}, {Error, State}) ->
+ {stop_and_reply, normal, {reply, From, {error, Error}}, State};
+error({call, From}, Msg, State) ->
+ handle_call(Msg, From, error, State);
+error(_, _, _) ->
+ {keep_state_and_data, [postpone]}.
+
+hello(internal, #client_hello{client_version = ClientVersion} = Hello,
+ #state{connection_states = ConnectionStates0,
+ port = Port, session = #session{own_certificate = Cert} = Session0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb,
+ ssl_options = SslOpts} = State) ->
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, HashSign} ->
- ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
+ ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt, HashSign},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
session = Session,
@@ -217,7 +217,7 @@ hello(Hello = #client_hello{client_version = ClientVersion},
#alert{} = Alert ->
handle_own_alert(Alert, ClientVersion, hello, State)
end;
-hello(Hello,
+hello(internal, Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
role = client,
@@ -230,20 +230,30 @@ hello(Hello,
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, 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,
+hello(info, Event, State) ->
+ handle_info(Event, hello, State);
+
+hello(Type, Event, State) ->
+ ssl_connection:hello(Type, Event, State, ?MODULE).
+
+abbreviated(info, Event, State) ->
+ handle_info(Event, abbreviated, State);
+abbreviated(Type, Event, State) ->
+ ssl_connection:abbreviated(Type, Event, State, ?MODULE).
+
+certify(info, Event, State) ->
+ handle_info(Event, certify, State);
+certify(Type, Event, State) ->
+ ssl_connection:certify(Type, Event, State, ?MODULE).
+
+cipher(info, Event, State) ->
+ handle_info(Event, cipher, State);
+cipher(Type, Event, State) ->
+ ssl_connection:cipher(Type, Event, State, ?MODULE).
+
+connection(info, Event, State) ->
+ handle_info(Event, connection, State);
+connection(internal, #hello_request{}, #state{host = Host, port = Port,
session = #session{own_certificate = Cert} = Session0,
session_cache = Cache, session_cache_cb = CacheCb,
ssl_options = SslOpts,
@@ -257,40 +267,30 @@ connection(#hello_request{}, #state{host = Host, port = Port,
next_record(
State1#state{session = Session0#session{session_id
= Hello#client_hello.session_id}}),
- next_state(connection, hello, Record, State);
+ next_event(hello, Record, State);
-connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
+connection(internal, #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});
+ {next_state, hello, State#state{allow_renegotiate = false}, [{next_event, internal, Hello}]};
+
-connection(#client_hello{}, #state{role = server, allow_renegotiate = false} = State0) ->
+connection(internal, #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);
+ State1 = send_alert(Alert, State0),
+ {Record, State} = ssl_connection:prepare_connection(State1, ?MODULE),
+ next_event(connection, Record, State);
-connection(Msg, State) ->
- ssl_connection:connection(Msg, State, tls_connection).
+connection(Type, Event, State) ->
+ ssl_connection:connection(Type, Event, State, ?MODULE).
-%%--------------------------------------------------------------------
-%% 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)}.
+downgrade(Type, Event, State) ->
+ ssl_connection:downgrade(Type, Event, State, ?MODULE).
-%%--------------------------------------------------------------------
-%% 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
@@ -301,26 +301,25 @@ handle_sync_event(Event, From, StateName, State) ->
%% 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);
-
+ case next_tls_record(Data, State0) of
+ {Record, State} ->
+ next_event(StateName, Record, State);
+ #alert{} = Alert ->
+ handle_normal_shutdown(Alert, StateName, State0),
+ {stop, {shutdown, own_alert}}
+ end;
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};
+ {stop, {shutdown, transport_closed}};
handle_info(Msg, StateName, State) ->
ssl_connection:handle_info(Msg, StateName, State).
+handle_call(Event, From, StateName, State) ->
+ ssl_connection:handle_call(Event, From, StateName, State, ?MODULE).
+
%%--------------------------------------------------------------------
%% 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
@@ -335,7 +334,10 @@ terminate(Reason, StateName, State) ->
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State, _Extra) ->
- {ok, StateName, State}.
+ {?GEN_STATEM_CB_MODE, StateName, State}.
+
+format_status(Type, Data) ->
+ ssl_connection:format_status(Type, Data).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -372,96 +374,28 @@ next_record(#state{socket = Socket,
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.
+next_event(StateName, Record, State) ->
+ next_event(StateName, Record, State, []).
+next_event(connection = StateName, no_record, State0, Actions) ->
+ case next_record_if_active(State0) of
+ {no_record, State} ->
+ ssl_connection:hibernate_after(StateName, State, Actions);
+ {#ssl_tls{} = Record, State} ->
+ {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
+ {#alert{} = Alert, State} ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end;
+next_event(StateName, Record, State, Actions) ->
+ case Record of
+ no_record ->
+ {next_state, StateName, State, Actions};
+ #ssl_tls{} = Record ->
+ {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
+ #alert{} = Alert ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end.
send_flight(Fragments, #state{transport_cb = Transport, socket = Socket,
protocol_buffers = _PBuffers} = State) ->
@@ -514,21 +448,23 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
renegotiation = {false, first},
allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,
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) ->
+ #alert{}; %% Place holder
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.
+next_record_if_active(State =
+ #state{socket_options =
+ #socket_options{active = false}}) ->
+ {no_record ,State};
+
+next_record_if_active(State) ->
+ next_record(State).
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 2530d66052..e79e1cede0 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -39,7 +39,7 @@
-export([encode_plain_text/4, encode_handshake/3, encode_change_cipher_spec/2]).
%% Protocol version handling
--export([protocol_version/1, lowest_protocol_version/2,
+-export([protocol_version/1, lowest_protocol_version/2, lowest_protocol_version/1,
highest_protocol_version/1, supported_protocol_versions/0,
is_acceptable_version/2]).
@@ -254,6 +254,18 @@ lowest_protocol_version(Version = {M,_}, {N, _}) when M > N ->
Version;
lowest_protocol_version(_,Version) ->
Version.
+
+%%--------------------------------------------------------------------
+-spec lowest_protocol_version([dtls_version()]) -> dtls_version().
+%%
+%% Description: Lowest protocol version present in a list
+%%--------------------------------------------------------------------
+lowest_protocol_version([]) ->
+ lowest_protocol_version();
+lowest_protocol_version(Versions) ->
+ [Ver | Vers] = Versions,
+ lowest_list_protocol_version(Ver, Vers).
+
%%--------------------------------------------------------------------
-spec highest_protocol_version([dtls_version()]) -> dtls_version().
%%
@@ -302,6 +314,12 @@ supported_protocol_versions([]) ->
supported_protocol_versions([_|_] = Vsns) ->
Vsns.
+%% highest_protocol_version() ->
+%% highest_protocol_version(supported_protocol_versions()).
+
+lowest_protocol_version() ->
+ lowest_protocol_version(supported_protocol_versions()).
+
supported_connection_protocol_versions([]) ->
?ALL_DATAGRAM_SUPPORTED_VERSIONS.
@@ -421,3 +439,8 @@ mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
calc_aad(Type, {MajVer, MinVer}, Epoch, SeqNo) ->
NewSeq = (Epoch bsl 48) + SeqNo,
<<NewSeq:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
+
+lowest_list_protocol_version(Ver, []) ->
+ Ver;
+lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 057906bcb3..11728128c4 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,9 +1,6 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"7\\.2">>, [{load_module, tls_connection, soft_purge, soft_purge, []},
- {load_module, ssl_tls_dist_proxy, soft_purge, soft_purge, []}
- ]},
{<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
@@ -11,9 +8,6 @@
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"7\\.2">>, [{load_module, tls_connection, soft_purge, soft_purge, []},
- {load_module, ssl_tls_dist_proxy, soft_purge, soft_purge, []}
- ]},
{<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 4bcd6ddb0e..dbbb25025c 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -42,7 +42,7 @@
renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
--export([random_bytes/1, handle_options/2]).
+-export([handle_options/2]).
-deprecated({negotiated_next_protocol, 1, next_major_release}).
-deprecated({connection_info, 1, next_major_release}).
@@ -581,22 +581,6 @@ format_error(Error) ->
Other
end.
-%%--------------------------------------------------------------------
--spec random_bytes(integer()) -> binary().
-
-%%
-%% Description: Generates cryptographically secure random sequence if possible
-%% fallbacks on pseudo random function
-%%--------------------------------------------------------------------
-random_bytes(N) ->
- try crypto:strong_rand_bytes(N) of
- RandBytes ->
- RandBytes
- catch
- error:low_entropy ->
- crypto:rand_bytes(N)
- end.
-
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -712,7 +696,7 @@ handle_options(Opts0, Role) ->
default_option_role(server, true, Role),
server, Role),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
- hibernate_after = handle_option(hibernate_after, Opts, undefined),
+ hibernate_after = handle_option(hibernate_after, Opts, infinity),
erl_dist = handle_option(erl_dist, Opts, false),
alpn_advertised_protocols =
handle_option(alpn_advertised_protocols, Opts, undefined),
@@ -901,10 +885,13 @@ validate_option(client_renegotiation, Value) when is_boolean(Value) ->
validate_option(renegotiate_at, Value) when is_integer(Value) ->
erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT);
-validate_option(hibernate_after, undefined) ->
- undefined;
+validate_option(hibernate_after, undefined) -> %% Backwards compatibility
+ infinity;
+validate_option(hibernate_after, infinity) ->
+ infinity;
validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
Value;
+
validate_option(erl_dist,Value) when is_boolean(Value) ->
Value;
validate_option(Opt, Value)
@@ -1105,10 +1092,7 @@ binary_cipher_suites(Version, []) ->
%% Defaults to all supported suites that does
%% not require explicit configuration
ssl_cipher:filter_suites(ssl_cipher:suites(Version));
-binary_cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
- Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0],
- binary_cipher_suites(Version, Ciphers);
-binary_cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
+binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) ->
Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index e66f253a70..dc0a0c2cc4 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,4 +1,4 @@
-%%
+%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
@@ -39,7 +39,8 @@
suite/1, suites/1, all_suites/1,
ec_keyed_suites/0, anonymous_suites/1, psk_suites/1, srp_suites/0,
rc4_suites/1, des_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
- hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1]).
+ hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1,
+ random_bytes/1]).
-export_type([cipher_suite/0,
erl_cipher_suite/0, openssl_cipher_suite/0,
@@ -49,7 +50,8 @@
| aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
-type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
-type sign_algo() :: rsa | dsa | ecdsa.
--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 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()} % Pre TLS 1.2
%% TLS 1.2, internally PRE TLS 1.2 will use default_prf
| {key_algo(), cipher(), hash(), hash() | default_prf}.
@@ -102,7 +104,7 @@ cipher_init(?RC4, IV, Key) ->
State = crypto:stream_init(rc4, Key),
#cipher_state{iv = IV, key = Key, state = State};
cipher_init(?AES_GCM, IV, Key) ->
- <<Nonce:64>> = ssl:random_bytes(8),
+ <<Nonce:64>> = random_bytes(8),
#cipher_state{iv = IV, key = Key, nonce = Nonce};
cipher_init(_BCA, IV, Key) ->
#cipher_state{iv = IV, key = Key}.
@@ -853,17 +855,17 @@ suite({rsa_psk, aes_256_cbc,sha}) ->
%%% TLS 1.2 PSK Cipher Suites RFC 5487
-suite({psk, aes_128_gcm, null}) ->
+suite({psk, aes_128_gcm, null, sha256}) ->
?TLS_PSK_WITH_AES_128_GCM_SHA256;
-suite({psk, aes_256_gcm, null}) ->
+suite({psk, aes_256_gcm, null, sha384}) ->
?TLS_PSK_WITH_AES_256_GCM_SHA384;
-suite({dhe_psk, aes_128_gcm, null}) ->
+suite({dhe_psk, aes_128_gcm, null, sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256;
-suite({dhe_psk, aes_256_gcm, null}) ->
+suite({dhe_psk, aes_256_gcm, null, sha384}) ->
?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384;
-suite({rsa_psk, aes_128_gcm, null}) ->
+suite({rsa_psk, aes_128_gcm, null, sha256}) ->
?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256;
-suite({rsa_psk, aes_256_gcm, null}) ->
+suite({rsa_psk, aes_256_gcm, null, sha384}) ->
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384;
suite({psk, aes_128_cbc, sha256}) ->
@@ -970,74 +972,74 @@ suite({ecdh_anon, aes_256_cbc, sha}) ->
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA;
%%% RFC 5289 EC TLS suites
-suite({ecdhe_ecdsa, aes_128_cbc, sha256}) ->
+suite({ecdhe_ecdsa, aes_128_cbc, sha256, sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
-suite({ecdhe_ecdsa, aes_256_cbc, sha384}) ->
+suite({ecdhe_ecdsa, aes_256_cbc, sha384, sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384;
-suite({ecdh_ecdsa, aes_128_cbc, sha256}) ->
+suite({ecdh_ecdsa, aes_128_cbc, sha256, sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256;
-suite({ecdh_ecdsa, aes_256_cbc, sha384}) ->
+suite({ecdh_ecdsa, aes_256_cbc, sha384, sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384;
-suite({ecdhe_rsa, aes_128_cbc, sha256}) ->
+suite({ecdhe_rsa, aes_128_cbc, sha256, sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256;
-suite({ecdhe_rsa, aes_256_cbc, sha384}) ->
+suite({ecdhe_rsa, aes_256_cbc, sha384, sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384;
-suite({ecdh_rsa, aes_128_cbc, sha256}) ->
+suite({ecdh_rsa, aes_128_cbc, sha256, sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;
-suite({ecdh_rsa, aes_256_cbc, sha384}) ->
+suite({ecdh_rsa, aes_256_cbc, sha384, sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384;
%% RFC 5288 AES-GCM Cipher Suites
-suite({rsa, aes_128_gcm, null}) ->
+suite({rsa, aes_128_gcm, null, sha256}) ->
?TLS_RSA_WITH_AES_128_GCM_SHA256;
-suite({rsa, aes_256_gcm, null}) ->
+suite({rsa, aes_256_gcm, null, sha384}) ->
?TLS_RSA_WITH_AES_256_GCM_SHA384;
-suite({dhe_rsa, aes_128_gcm, null}) ->
+suite({dhe_rsa, aes_128_gcm, null, sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
-suite({dhe_rsa, aes_256_gcm, null}) ->
+suite({dhe_rsa, aes_256_gcm, null, sha384}) ->
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
-suite({dh_rsa, aes_128_gcm, null}) ->
+suite({dh_rsa, aes_128_gcm, null, sha256}) ->
?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
-suite({dh_rsa, aes_256_gcm, null}) ->
+suite({dh_rsa, aes_256_gcm, null, sha384}) ->
?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
-suite({dhe_dss, aes_128_gcm, null}) ->
+suite({dhe_dss, aes_128_gcm, null, sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
-suite({dhe_dss, aes_256_gcm, null}) ->
+suite({dhe_dss, aes_256_gcm, null, sha384}) ->
?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
-suite({dh_dss, aes_128_gcm, null}) ->
+suite({dh_dss, aes_128_gcm, null, sha256}) ->
?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
-suite({dh_dss, aes_256_gcm, null}) ->
+suite({dh_dss, aes_256_gcm, null, sha384}) ->
?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
-suite({dh_anon, aes_128_gcm, null}) ->
+suite({dh_anon, aes_128_gcm, null, sha256}) ->
?TLS_DH_anon_WITH_AES_128_GCM_SHA256;
-suite({dh_anon, aes_256_gcm, null}) ->
+suite({dh_anon, aes_256_gcm, null, sha384}) ->
?TLS_DH_anon_WITH_AES_256_GCM_SHA384;
%% RFC 5289 ECC AES-GCM Cipher Suites
-suite({ecdhe_ecdsa, aes_128_gcm, null}) ->
+suite({ecdhe_ecdsa, aes_128_gcm, null, sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
-suite({ecdhe_ecdsa, aes_256_gcm, null}) ->
+suite({ecdhe_ecdsa, aes_256_gcm, null, sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
-suite({ecdh_ecdsa, aes_128_gcm, null}) ->
+suite({ecdh_ecdsa, aes_128_gcm, null, sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
-suite({ecdh_ecdsa, aes_256_gcm, null}) ->
+suite({ecdh_ecdsa, aes_256_gcm, null, sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
-suite({ecdhe_rsa, aes_128_gcm, null}) ->
+suite({ecdhe_rsa, aes_128_gcm, null, sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
-suite({ecdhe_rsa, aes_256_gcm, null}) ->
+suite({ecdhe_rsa, aes_256_gcm, null, sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
-suite({ecdh_rsa, aes_128_gcm, null}) ->
+suite({ecdh_rsa, aes_128_gcm, null, sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
-suite({ecdh_rsa, aes_256_gcm, null}) ->
+suite({ecdh_rsa, aes_256_gcm, null, sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
-suite({ecdhe_rsa, chacha20_poly1305, null}) ->
+suite({ecdhe_rsa, chacha20_poly1305, null, sha256}) ->
?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
-suite({ecdhe_ecdsa, chacha20_poly1305, null}) ->
+suite({ecdhe_ecdsa, chacha20_poly1305, null, sha256}) ->
?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
-suite({dhe_rsa, chacha20_poly1305, null}) ->
+suite({dhe_rsa, chacha20_poly1305, null, sha256}) ->
?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256.
%%--------------------------------------------------------------------
@@ -1472,6 +1474,16 @@ is_acceptable_prf(Prf, Algos) ->
is_fallback(CipherSuites)->
lists:member(?TLS_FALLBACK_SCSV, CipherSuites).
+
+%%--------------------------------------------------------------------
+-spec random_bytes(integer()) -> binary().
+
+%%
+%% Description: Generates cryptographically secure random sequence
+%%--------------------------------------------------------------------
+random_bytes(N) ->
+ crypto:strong_rand_bytes(N).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -1712,7 +1724,7 @@ get_padding_aux(BlockSize, PadLength) ->
random_iv(IV) ->
IVSz = byte_size(IV),
- ssl:random_bytes(IVSz).
+ random_bytes(IVSz).
next_iv(Bin, IV) ->
BinSz = byte_size(Bin),
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 1568e8559f..26c371a8ea 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -38,7 +38,7 @@
%% Setup
-export([connect/8, ssl_accept/7, handshake/2, handshake/3,
- socket_control/4, socket_control/5]).
+ socket_control/4, socket_control/5, start_or_recv_cancel_timer/2]).
%% User Events
-export([send/2, recv/3, close/2, shutdown/2,
@@ -47,12 +47,16 @@
connection_information/1
]).
--export([handle_session/7]).
+%% General gen_statem state functions with extra callback argument
+%% to determine if it is an SSL/TLS or DTLS gen_statem machine
+-export([init/4, hello/4, abbreviated/4, certify/4, cipher/4, connection/4, downgrade/4]).
-%% SSL FSM state functions
--export([hello/3, abbreviated/3, certify/3, cipher/3, connection/3]).
-%% SSL all state functions
--export([handle_sync_event/4, handle_info/3, terminate/3, format_status/2]).
+%% gen_statem callbacks
+-export([terminate/3, format_status/2]).
+
+%%
+-export([handle_info/3, handle_call/5, handle_session/7, ssl_config/3,
+ prepare_connection/2, hibernate_after/3]).
%%====================================================================
@@ -100,7 +104,7 @@ ssl_accept(Connection, Port, Socket, Opts, User, CbInfo, Timeout) ->
%% Description: Starts ssl handshake.
%%--------------------------------------------------------------------
handshake(#sslsocket{pid = Pid}, Timeout) ->
- case sync_send_all_state_event(Pid, {start, Timeout}) of
+ case call(Pid, {start, Timeout}) of
connected ->
ok;
Error ->
@@ -114,7 +118,7 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
%% Description: Starts ssl handshake with some new options
%%--------------------------------------------------------------------
handshake(#sslsocket{pid = Pid}, SslOptions, Timeout) ->
- case sync_send_all_state_event(Pid, {start, SslOptions, Timeout}) of
+ case call(Pid, {start, SslOptions, Timeout}) of
connected ->
ok;
Error ->
@@ -148,7 +152,7 @@ socket_control(Connection, Socket, Pid, Transport, ListenTracker) ->
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
send(Pid, Data) ->
- sync_send_all_state_event(Pid, {application_data,
+ call(Pid, {application_data,
%% iolist_to_binary should really
%% be called iodata_to_binary()
erlang:iolist_to_binary(Data)}).
@@ -160,7 +164,7 @@ send(Pid, Data) ->
%% Description: Receives data when active = false
%%--------------------------------------------------------------------
recv(Pid, Length, Timeout) ->
- sync_send_all_state_event(Pid, {recv, Length, Timeout}).
+ call(Pid, {recv, Length, Timeout}).
%%--------------------------------------------------------------------
-spec connection_information(pid()) -> {ok, list()} | {error, reason()}.
@@ -168,7 +172,7 @@ recv(Pid, Length, Timeout) ->
%% Description: Get the SNI hostname
%%--------------------------------------------------------------------
connection_information(Pid) when is_pid(Pid) ->
- sync_send_all_state_event(Pid, connection_information).
+ call(Pid, connection_information).
%%--------------------------------------------------------------------
-spec close(pid(), {close, Timeout::integer() |
@@ -178,7 +182,7 @@ connection_information(Pid) when is_pid(Pid) ->
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
close(ConnectionPid, How) ->
- case sync_send_all_state_event(ConnectionPid, How) of
+ case call(ConnectionPid, How) of
{error, closed} ->
ok;
Other ->
@@ -190,7 +194,7 @@ close(ConnectionPid, How) ->
%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
shutdown(ConnectionPid, How) ->
- sync_send_all_state_event(ConnectionPid, {shutdown, How}).
+ call(ConnectionPid, {shutdown, How}).
%%--------------------------------------------------------------------
-spec new_user(pid(), pid()) -> ok | {error, reason()}.
@@ -199,7 +203,7 @@ shutdown(ConnectionPid, How) ->
%% or once.
%%--------------------------------------------------------------------
new_user(ConnectionPid, User) ->
- sync_send_all_state_event(ConnectionPid, {new_user, User}).
+ call(ConnectionPid, {new_user, User}).
%%--------------------------------------------------------------------
-spec negotiated_protocol(pid()) -> {ok, binary()} | {error, reason()}.
@@ -207,7 +211,7 @@ new_user(ConnectionPid, User) ->
%% Description: Returns the negotiated protocol
%%--------------------------------------------------------------------
negotiated_protocol(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, negotiated_protocol).
+ call(ConnectionPid, negotiated_protocol).
%%--------------------------------------------------------------------
-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}.
@@ -215,14 +219,14 @@ negotiated_protocol(ConnectionPid) ->
%% Description: Same as inet:getopts/2
%%--------------------------------------------------------------------
get_opts(ConnectionPid, OptTags) ->
- sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}).
+ call(ConnectionPid, {get_opts, OptTags}).
%%--------------------------------------------------------------------
-spec set_opts(pid(), list()) -> ok | {error, reason()}.
%%
%% Description: Same as inet:setopts/2
%%--------------------------------------------------------------------
set_opts(ConnectionPid, Options) ->
- sync_send_all_state_event(ConnectionPid, {set_opts, Options}).
+ call(ConnectionPid, {set_opts, Options}).
%%--------------------------------------------------------------------
-spec session_info(pid()) -> {ok, list()} | {error, reason()}.
@@ -230,7 +234,7 @@ set_opts(ConnectionPid, Options) ->
%% Description: Returns info about the ssl session
%%--------------------------------------------------------------------
session_info(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, session_info).
+ call(ConnectionPid, session_info).
%%--------------------------------------------------------------------
-spec peer_certificate(pid()) -> {ok, binary()| undefined} | {error, reason()}.
@@ -238,7 +242,7 @@ session_info(ConnectionPid) ->
%% Description: Returns the peer cert
%%--------------------------------------------------------------------
peer_certificate(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, peer_certificate).
+ call(ConnectionPid, peer_certificate).
%%--------------------------------------------------------------------
-spec renegotiation(pid()) -> ok | {error, reason()}.
@@ -246,7 +250,7 @@ peer_certificate(ConnectionPid) ->
%% Description: Starts a renegotiation of the ssl session.
%%--------------------------------------------------------------------
renegotiation(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, renegotiate).
+ call(ConnectionPid, renegotiate).
%%--------------------------------------------------------------------
-spec prf(pid(), binary() | 'master_secret', binary(),
@@ -256,9 +260,13 @@ renegotiation(ConnectionPid) ->
%% Description: use a ssl sessions TLS PRF to generate key material
%%--------------------------------------------------------------------
prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
- sync_send_all_state_event(ConnectionPid, {prf, Secret, Label, Seed, WantedLength}).
-
+ call(ConnectionPid, {prf, Secret, Label, Seed, WantedLength}).
+%%--------------------------------------------------------------------
+-spec handle_session(#server_hello{}, ssl_record:ssl_version(),
+ binary(), #connection_states{}, _,_, #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
handle_session(#server_hello{cipher_suite = CipherSuite,
compression_method = Compression},
Version, NewId, ConnectionStates, ProtoExt, Protocol0,
@@ -290,61 +298,104 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
handle_resumed_session(NewId,
State#state{connection_states = ConnectionStates})
end.
-
+
%%--------------------------------------------------------------------
--spec hello(start | #hello_request{} | #server_hello{} | term(),
- #state{}, tls_connection | dtls_connection) ->
- gen_fsm_state_return().
+-spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}.
%%--------------------------------------------------------------------
-hello(start, #state{role = server} = State0, Connection) ->
- {Record, State} = Connection:next_record(State0),
- Connection:next_state(hello, hello, Record, State);
+ssl_config(Opts, Role, State) ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo,
+ OwnCert, Key, DHParams} =
+ ssl_config:init(Opts, Role),
+ Handshake = ssl_handshake:init_handshake_history(),
+ TimeStamp = erlang:monotonic_time(),
+ Session = State#state.session,
+ State#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,
+ crl_db = CRLDbInfo,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = Opts}.
-hello(#hello_request{}, #state{role = client} = State0, Connection) ->
- {Record, State} = Connection:next_record(State0),
- Connection:next_state(hello, hello, Record, State);
+%%====================================================================
+%% gen_statem state functions
+%%====================================================================
+%%--------------------------------------------------------------------
+-spec init(gen_statem:event_type(),
+ {start, timeout()} | {start, {list(), list()}, timeout()}| term(),
+ #state{}, tls_connection | dtls_connection) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
-hello({common_client_hello, Type, ServerHelloExt},
- State, Connection) ->
+init({call, From}, {start, Timeout}, State0, Connection) ->
+ Timer = start_or_recv_cancel_timer(Timeout, From),
+ {Record, State} = Connection:next_record(State0#state{start_or_recv_from = From,
+ timer = Timer}),
+ Connection:next_event(hello, Record, State);
+init({call, From}, {start, {Opts, EmOpts}, Timeout},
+ #state{role = Role} = State0, Connection) ->
+ try
+ State = ssl_config(Opts, Role, State0),
+ init({call, From}, {start, Timeout},
+ State#state{ssl_options = Opts, socket_options = EmOpts}, Connection)
+ catch throw:Error ->
+ {stop_and_reply, normal, {reply, From, {error, Error}}}
+ end;
+init({call, From}, Msg, State, Connection) ->
+ handle_call(Msg, From, init, State, Connection);
+init(_Type, _Event, _State, _Connection) ->
+ {keep_state_and_data, [postpone]}.
+
+%%--------------------------------------------------------------------
+-spec hello(gen_statem:event_type(),
+ #hello_request{} | #server_hello{} | term(),
+ #state{}, tls_connection | dtls_connection) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+hello({call, From}, Msg, State, Connection) ->
+ handle_call(Msg, From, hello, State, Connection);
+hello(internal, {common_client_hello, Type, ServerHelloExt}, State, Connection) ->
do_server_hello(Type, ServerHelloExt, State, Connection);
-hello(timeout, State, _) ->
- {next_state, hello, State, hibernate};
-
-hello(Msg, State, Connection) ->
- Connection:handle_unexpected_message(Msg, hello, State).
+hello(info, Msg, State, _) ->
+ handle_info(Msg, hello, State);
+hello(Type, Msg, State, Connection) ->
+ handle_common_event(Type, Msg, hello, State, Connection).
%%--------------------------------------------------------------------
--spec abbreviated(#hello_request{} | #finished{} | term(),
+-spec abbreviated(gen_statem:event_type(),
+ #hello_request{} | #finished{} | term(),
#state{}, tls_connection | dtls_connection) ->
- gen_fsm_state_return().
+ gen_statem:state_function_result().
%%--------------------------------------------------------------------
-abbreviated(#hello_request{}, State0, Connection) ->
- {Record, State} = Connection:next_record(State0),
- Connection:next_state(abbreviated, hello, Record, State);
+abbreviated({call, From}, Msg, State, Connection) ->
+ handle_call(Msg, From, abbreviated, State, Connection);
-abbreviated(#finished{verify_data = Data} = Finished,
+abbreviated(internal, #finished{verify_data = Data} = Finished,
#state{role = server,
negotiated_version = Version,
expecting_finished = true,
tls_handshake_history = Handshake,
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
- State, Connection) ->
+ State0, Connection) ->
case ssl_handshake:verify_connection(Version, Finished, client,
get_current_prf(ConnectionStates0, write),
MasterSecret, Handshake) of
verified ->
ConnectionStates =
ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0),
- Connection:next_state_connection(abbreviated,
- ack_connection(
- State#state{connection_states = ConnectionStates,
- expecting_finished = false}));
+ {Record, State} = prepare_connection(State0#state{connection_states = ConnectionStates,
+ expecting_finished = false}, Connection),
+ Connection:next_event(connection, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, abbreviated, State)
+ Connection:handle_own_alert(Alert, Version, abbreviated, State0)
end;
-abbreviated(#finished{verify_data = Data} = Finished,
+abbreviated(internal, #finished{verify_data = Data} = Finished,
#state{role = client, tls_handshake_history = Handshake0,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
@@ -355,40 +406,49 @@ abbreviated(#finished{verify_data = Data} = Finished,
verified ->
ConnectionStates1 =
ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
- State =
+ State1 =
finalize_handshake(State0#state{connection_states = ConnectionStates1},
abbreviated, Connection),
- Connection:next_state_connection(abbreviated,
- ack_connection(State#state{expecting_finished = false}));
- #alert{} = Alert ->
+ {Record, State} = prepare_connection(State1#state{expecting_finished = false}, Connection),
+ Connection:next_event(connection, Record, State);
+ #alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, abbreviated, State0)
end;
%% only allowed to send next_protocol message after change cipher spec
%% & before finished message and it is not allowed during renegotiation
-abbreviated(#next_protocol{selected_protocol = SelectedProtocol},
+abbreviated(internal, #next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true} = State0,
Connection) ->
- {Record, State} = Connection:next_record(State0#state{negotiated_protocol = SelectedProtocol}),
- Connection:next_state(abbreviated, abbreviated, Record, State#state{expecting_next_protocol_negotiation = false});
-
-abbreviated(timeout, State, _) ->
- {next_state, abbreviated, State, hibernate };
-
-abbreviated(Msg, State, Connection) ->
- Connection:handle_unexpected_message(Msg, abbreviated, State).
-
+ {Record, State} =
+ Connection:next_record(State0#state{negotiated_protocol = SelectedProtocol}),
+ Connection:next_event(abbreviated, Record,
+ State#state{expecting_next_protocol_negotiation = false});
+abbreviated(internal,
+ #change_cipher_spec{type = <<1>>}, #state{connection_states = ConnectionStates0} =
+ State0, Connection) ->
+ ConnectionStates1 =
+ ssl_record:activate_pending_connection_state(ConnectionStates0, read),
+ {Record, State} = Connection:next_record(State0#state{connection_states =
+ ConnectionStates1}),
+ Connection:next_event(abbreviated, Record, State#state{expecting_finished = true});
+abbreviated(info, Msg, State, _) ->
+ handle_info(Msg, abbreviated, State);
+abbreviated(Type, Msg, State, Connection) ->
+ handle_common_event(Type, Msg, abbreviated, State, Connection).
+
%%--------------------------------------------------------------------
--spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} |
+-spec certify(gen_statem:event_type(),
+ #hello_request{} | #certificate{} | #server_key_exchange{} |
#certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(),
#state{}, tls_connection | dtls_connection) ->
- gen_fsm_state_return().
+ gen_statem:state_function_result().
%%--------------------------------------------------------------------
-certify(#hello_request{}, State0, Connection) ->
- {Record, State} = Connection:next_record(State0),
- Connection:next_state(certify, hello, Record, State);
-
-certify(#certificate{asn1_certificates = []},
+certify({call, From}, Msg, State, Connection) ->
+ handle_call(Msg, From, certify, State, Connection);
+certify(info, Msg, State, _) ->
+ handle_info(Msg, certify, State);
+certify(internal, #certificate{asn1_certificates = []},
#state{role = server, negotiated_version = Version,
ssl_options = #ssl_options{verify = verify_peer,
fail_if_no_peer_cert = true}} =
@@ -396,15 +456,16 @@ certify(#certificate{asn1_certificates = []},
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
Connection:handle_own_alert(Alert, Version, certify, State);
-certify(#certificate{asn1_certificates = []},
+certify(internal, #certificate{asn1_certificates = []},
#state{role = server,
ssl_options = #ssl_options{verify = verify_peer,
fail_if_no_peer_cert = false}} =
State0, Connection) ->
- {Record, State} = Connection:next_record(State0#state{client_certificate_requested = false}),
- Connection:next_state(certify, certify, Record, State);
+ {Record, State} =
+ Connection:next_record(State0#state{client_certificate_requested = false}),
+ Connection:next_event(certify, Record, State);
-certify(#certificate{} = Cert,
+certify(internal, #certificate{} = Cert,
#state{negotiated_version = Version,
role = Role,
cert_db = CertDbHandle,
@@ -426,7 +487,7 @@ certify(#certificate{} = Cert,
Connection:handle_own_alert(Alert, Version, certify, State)
end;
-certify(#server_key_exchange{exchange_keys = Keys},
+certify(internal, #server_key_exchange{exchange_keys = Keys},
#state{role = client, negotiated_version = Version,
key_algorithm = Alg,
public_key_info = PubKeyInfo,
@@ -438,28 +499,28 @@ certify(#server_key_exchange{exchange_keys = Keys},
Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
Params = ssl_handshake:decode_server_key(Keys, Alg, Version),
+
%% Use negotiated value if TLS-1.2 otherwhise return default
HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, Version),
+
case is_anonymous(Alg) of
true ->
calculate_secret(Params#server_key_params.params,
State#state{hashsign_algorithm = HashSign}, Connection);
false ->
- case ssl_handshake:verify_server_key(Params, HashSign, ConnectionStates, Version, PubKeyInfo) of
+ case ssl_handshake:verify_server_key(Params, HashSign,
+ ConnectionStates, Version, PubKeyInfo) of
true ->
calculate_secret(Params#server_key_params.params,
- State#state{hashsign_algorithm = HashSign}, Connection);
+ State#state{hashsign_algorithm = HashSign},
+ Connection);
false ->
Connection:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
Version, certify, State)
end
end;
-certify(#server_key_exchange{} = Msg,
- #state{role = client, key_algorithm = rsa} = State, Connection) ->
- Connection:handle_unexpected_message(Msg, certify_server_keyexchange, State);
-
-certify(#certificate_request{hashsign_algorithms = HashSigns},
+certify(internal, #certificate_request{hashsign_algorithms = HashSigns},
#state{session = #session{own_certificate = Cert},
key_algorithm = KeyExAlg,
ssl_options = #ssl_options{signature_algs = SupportedHashSigns},
@@ -470,12 +531,12 @@ certify(#certificate_request{hashsign_algorithms = HashSigns},
Connection:handle_own_alert(Alert, Version, certify, State0);
NegotiatedHashSign ->
{Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
- Connection:next_state(certify, certify, Record,
+ Connection:next_event(certify, Record,
State#state{cert_hashsign_algorithm = NegotiatedHashSign})
end;
%% PSK and RSA_PSK might bypass the Server-Key-Exchange
-certify(#server_hello_done{},
+certify(internal, #server_hello_done{},
#state{session = #session{master_secret = undefined},
negotiated_version = Version,
psk_identity = PSKIdentity,
@@ -493,27 +554,29 @@ certify(#server_hello_done{},
client_certify_and_key_exchange(State, Connection)
end;
-certify(#server_hello_done{},
+certify(internal, #server_hello_done{},
#state{session = #session{master_secret = undefined},
ssl_options = #ssl_options{user_lookup_fun = PSKLookup},
- negotiated_version = {Major, Minor},
+ negotiated_version = {Major, Minor} = Version,
psk_identity = PSKIdentity,
premaster_secret = undefined,
role = client,
key_algorithm = Alg} = State0, Connection)
when Alg == rsa_psk ->
- Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
+ Rand = ssl_cipher:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
RSAPremasterSecret = <<?BYTE(Major), ?BYTE(Minor), Rand/binary>>,
- case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup, RSAPremasterSecret) of
+ case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup,
+ RSAPremasterSecret) of
#alert{} = Alert ->
- Alert;
+ Connection:handle_own_alert(Alert, Version, certify, State0);
PremasterSecret ->
- State = master_secret(PremasterSecret, State0#state{premaster_secret = RSAPremasterSecret}),
+ State = master_secret(PremasterSecret,
+ State0#state{premaster_secret = RSAPremasterSecret}),
client_certify_and_key_exchange(State, Connection)
end;
%% Master secret was determined with help of server-key exchange msg
-certify(#server_hello_done{},
+certify(internal, #server_hello_done{},
#state{session = #session{master_secret = MasterSecret} = Session,
connection_states = ConnectionStates0,
negotiated_version = Version,
@@ -529,7 +592,7 @@ certify(#server_hello_done{},
end;
%% Master secret is calculated from premaster_secret
-certify(#server_hello_done{},
+certify(internal, #server_hello_done{},
#state{session = Session0,
connection_states = ConnectionStates0,
negotiated_version = Version,
@@ -546,14 +609,15 @@ certify(#server_hello_done{},
Connection:handle_own_alert(Alert, Version, certify, State0)
end;
-certify(#client_key_exchange{} = Msg,
+certify(internal = Type, #client_key_exchange{} = Msg,
#state{role = server,
client_certificate_requested = true,
- ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State, Connection) ->
+ ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State,
+ Connection) ->
%% We expect a certificate here
- Connection:handle_unexpected_message(Msg, certify_client_key_exchange, State);
+ handle_common_event(Type, Msg, certify, State, Connection);
-certify(#client_key_exchange{exchange_keys = Keys},
+certify(internal, #client_key_exchange{exchange_keys = Keys},
State = #state{key_algorithm = KeyAlg, negotiated_version = Version}, Connection) ->
try
certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version),
@@ -563,22 +627,23 @@ certify(#client_key_exchange{exchange_keys = Keys},
Connection:handle_own_alert(Alert, Version, certify, State)
end;
-certify(timeout, State, _) ->
- {next_state, certify, State, hibernate};
-
-certify(Msg, State, Connection) ->
- Connection:handle_unexpected_message(Msg, certify, State).
-
+certify(Type, Msg, State, Connection) ->
+ handle_common_event(Type, Msg, certify, State, Connection).
+
%%--------------------------------------------------------------------
--spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(),
+-spec cipher(gen_statem:event_type(),
+ #hello_request{} | #certificate_verify{} | #finished{} | term(),
#state{}, tls_connection | dtls_connection) ->
- gen_fsm_state_return().
+ gen_statem:state_function_result().
%%--------------------------------------------------------------------
-cipher(#hello_request{}, State0, Connection) ->
- {Record, State} = Connection:next_record(State0),
- Connection:next_state(cipher, hello, Record, State);
+cipher({call, From}, Msg, State, Connection) ->
+ handle_call(Msg, From, cipher, State, Connection);
-cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign},
+cipher(info, Msg, State, _) ->
+ handle_info(Msg, cipher, State);
+
+cipher(internal, #certificate_verify{signature = Signature,
+ hashsign_algorithm = CertHashSign},
#state{role = server,
key_algorithm = KexAlg,
public_key_info = PublicKeyInfo,
@@ -593,19 +658,20 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
Version, HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = Connection:next_record(State0),
- Connection:next_state(cipher, cipher, Record,
+ Connection:next_event(cipher, Record,
State#state{cert_hashsign_algorithm = HashSign});
#alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, cipher, State0)
end;
%% client must send a next protocol message if we are expecting it
-cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true,
- negotiated_protocol = undefined, negotiated_version = Version} = State0,
+cipher(internal, #finished{},
+ #state{role = server, expecting_next_protocol_negotiation = true,
+ negotiated_protocol = undefined, negotiated_version = Version} = State0,
Connection) ->
Connection:handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0);
-cipher(#finished{verify_data = Data} = Finished,
+cipher(internal, #finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
host = Host,
port = Port,
@@ -621,109 +687,154 @@ cipher(#finished{verify_data = Data} = Finished,
MasterSecret, Handshake0) of
verified ->
Session = register_session(Role, Host, Port, Session0),
- cipher_role(Role, Data, Session, State#state{expecting_finished = false}, Connection);
+ cipher_role(Role, Data, Session,
+ State#state{expecting_finished = false}, Connection);
#alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, cipher, State)
end;
%% only allowed to send next_protocol message after change cipher spec
%% & before finished message and it is not allowed during renegotiation
-cipher(#next_protocol{selected_protocol = SelectedProtocol},
+cipher(internal, #next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true,
expecting_finished = true} = State0, Connection) ->
- {Record, State} = Connection:next_record(State0#state{negotiated_protocol = SelectedProtocol}),
- Connection:next_state(cipher, cipher, Record, State#state{expecting_next_protocol_negotiation = false});
-
-cipher(timeout, State, _) ->
- {next_state, cipher, State, hibernate};
-
-cipher(Msg, State, Connection) ->
- Connection:handle_unexpected_message(Msg, cipher, State).
+ {Record, State} =
+ Connection:next_record(State0#state{negotiated_protocol = SelectedProtocol}),
+ Connection:next_event(cipher, Record,
+ State#state{expecting_next_protocol_negotiation = false});
+cipher(internal, #change_cipher_spec{type = <<1>>}, #state{connection_states = ConnectionStates0} =
+ State0, Connection) ->
+ ConnectionStates1 =
+ ssl_record:activate_pending_connection_state(ConnectionStates0, read),
+ {Record, State} = Connection:next_record(State0#state{connection_states =
+ ConnectionStates1}),
+ Connection:next_event(cipher, Record, State#state{expecting_finished = true});
+cipher(Type, Msg, State, Connection) ->
+ handle_common_event(Type, Msg, cipher, State, Connection).
%%--------------------------------------------------------------------
--spec connection(term(), #state{}, tls_connection | dtls_connection) ->
- gen_fsm_state_return().
+-spec connection(gen_statem:event_type(), term(),
+ #state{}, tls_connection | dtls_connection) ->
+ gen_statem:state_function_result().
%%--------------------------------------------------------------------
-connection(timeout, State, _) ->
- {next_state, connection, State, hibernate};
+connection({call, From}, {application_data, Data},
+ #state{protocol_cb = Connection} = State, Connection) ->
+ %% We should look into having a worker process to do this to
+ %% parallize send and receive decoding and not block the receiver
+ %% if sending is overloading the socket.
+ try
+ Connection:write_application_data(Data, From, State)
+ catch throw:Error ->
+ hibernate_after(connection, State, [{reply, From, Error}])
+ end;
+connection({call, RecvFrom}, {recv, N, Timeout},
+ #state{protocol_cb = Connection, socket_options =
+ #socket_options{active = false}} = State0, Connection) ->
+ Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
+ Connection:passive_receive(State0#state{bytes_to_read = N,
+ start_or_recv_from = RecvFrom,
+ timer = Timer}, connection);
+connection({call, From}, renegotiate, #state{protocol_cb = Connection} = State,
+ Connection) ->
+ Connection:renegotiate(State#state{renegotiation = {true, From}}, []);
+connection({call, From}, peer_certificate,
+ #state{session = #session{peer_certificate = Cert}} = State, _) ->
+ hibernate_after(connection, State, [{reply, From, {ok, Cert}}]);
+connection({call, From}, connection_information, State, _) ->
+ Info = connection_info(State),
+ hibernate_after(connection, State, [{reply, From, {ok, Info}}]);
+connection({call, From}, session_info, #state{session = #session{session_id = Id,
+ cipher_suite = Suite}} = State, _) ->
+ SessionInfo = [{session_id, Id},
+ {cipher_suite, ssl_cipher:erl_suite_definition(Suite)}],
+ hibernate_after(connection, State, [{reply, From, SessionInfo}]);
+connection({call, From}, negotiated_protocol,
+ #state{negotiated_protocol = undefined} = State, _) ->
+ hibernate_after(connection, State, [{reply, From, {error, protocol_not_negotiated}}]);
+connection({call, From}, negotiated_protocol,
+ #state{negotiated_protocol = SelectedProtocol} = State, _) ->
+ hibernate_after(connection, State,
+ [{reply, From, {ok, SelectedProtocol}}]);
+connection({call, From}, Msg, State, Connection) ->
+ handle_call(Msg, From, connection, State, Connection);
+connection(info, Msg, State, _) ->
+ handle_info(Msg, connection, State);
+connection(internal, {recv, _}, State, Connection) ->
+ Connection:passive_receive(State, connection);
+connection(Type, Msg, State, Connection) ->
+ handle_common_event(Type, Msg, connection, State, Connection).
-connection(Msg, State, Connection) ->
- Connection:handle_unexpected_message(Msg, connection, State).
+%%--------------------------------------------------------------------
+-spec downgrade(gen_statem:event_type(), term(),
+ #state{}, tls_connection | dtls_connection) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+downgrade(internal, #alert{description = ?CLOSE_NOTIFY},
+ #state{transport_cb = Transport, socket = Socket,
+ downgrade = {Pid, From}} = State, _) ->
+ ssl_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]),
+ Transport:controlling_process(Socket, Pid),
+ gen_statem:reply(From, {ok, Socket}),
+ {stop, normal, State};
+downgrade(timeout, downgrade, #state{downgrade = {_, From}} = State, _) ->
+ gen_statem:reply(From, {error, timeout}),
+ {stop, normal, State};
+downgrade(Type, Event, State, Connection) ->
+ handle_common_event(Type, Event, downgrade, State, Connection).
%%--------------------------------------------------------------------
-%% 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.
+%% Event handling functions called by state functions to handle
+%% common or unexpected events for the state.
%%--------------------------------------------------------------------
-handle_sync_event({application_data, Data}, From, connection,
- #state{protocol_cb = Connection} = State) ->
- %% We should look into having a worker process to do this to
- %% parallize send and receive decoding and not block the receiver
- %% if sending is overloading the socket.
- try
- Connection:write_application_data(Data, From, State)
- catch throw:Error ->
- {reply, Error, connection, State, get_timeout(State)}
+handle_common_event(internal, {tls_record, TLSRecord}, StateName, State, Connection) ->
+ Connection:handle_common_event(internal, TLSRecord, StateName, State);
+handle_common_event(internal, #hello_request{}, StateName, #state{role = client} = State0, Connection)
+ when StateName =:= connection ->
+ {Record, State} = Connection:next_record(State0),
+ Connection:next_event(StateName, Record, State);
+handle_common_event(timeout, hibernate, _, _, _) ->
+ {keep_state_and_data, [hibernate]};
+handle_common_event(internal, {application_data, Data}, StateName, State0, Connection) ->
+ case Connection:read_application_data(Data, State0) of
+ {stop, Reason, State} ->
+ {stop, Reason, State};
+ {Record, State} ->
+ Connection:next_event(StateName, Record, State)
end;
-handle_sync_event({application_data, Data}, From, StateName,
- #state{send_queue = Queue} = State) ->
+handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName,
+ #state{negotiated_version = Version} = State, Connection) ->
+ Connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version,
+ StateName, State);
+handle_common_event(internal, _, _, _, _) ->
+ {keep_state_and_data, [postpone]};
+handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State,
+ Connection) ->
+ Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
+ Connection:handle_own_alert(Alert, Version, {StateName, Msg}, State).
+
+handle_call({application_data, _Data}, _, _, _, _) ->
%% In renegotiation priorities handshake, send data when handshake is finished
- {next_state, StateName,
- State#state{send_queue = queue:in({From, Data}, Queue)},
- get_timeout(State)};
-
-handle_sync_event({start, Timeout}, StartFrom, hello, #state{role = Role,
- protocol_cb = Connection,
- ssl_options = SSLOpts} = State0) ->
- try
- State = ssl_config(SSLOpts, Role, State0),
- Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
- Connection:hello(start, State#state{start_or_recv_from = StartFrom,
- timer = Timer})
- catch throw:Error ->
- {stop, normal, {error, Error}, State0}
- end;
-
-handle_sync_event({start, {Opts, EmOpts}, Timeout}, From, StateName, State) ->
- try
- handle_sync_event({start, Timeout}, From, StateName, State#state{socket_options = EmOpts,
- ssl_options = Opts})
- catch throw:Error ->
- {stop, normal, {error, Error}, State}
- end;
-
-%% These two clauses below could happen if a server upgrades a socket in
-%% active mode. Note that in this case we are lucky that
-%% controlling_process has been evalueated before receiving handshake
-%% messages from client. The server should put the socket in passive
-%% mode before telling the client that it is willing to upgrade
-%% and before calling ssl:ssl_accept/2. These clauses are
-%% here to make sure it is the users problem and not owers if
-%% they upgrade an active socket.
-handle_sync_event({start,_}, _, connection, State) ->
- {reply, connected, connection, State, get_timeout(State)};
-
-handle_sync_event({start, Timeout}, StartFrom, StateName, #state{role = Role, ssl_options = SslOpts} = State0) ->
- try
- State = ssl_config(SslOpts, Role, State0),
- Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
- {next_state, StateName, State#state{start_or_recv_from = StartFrom,
- timer = Timer}, get_timeout(State)}
- catch throw:Error ->
- {stop, normal, {error, Error}, State0}
- end;
-
-handle_sync_event({close, _} = Close, _, StateName, #state{protocol_cb = Connection} = State) ->
+ {keep_state_and_data, [postpone]};
+handle_call({close, {Pid, Timeout}}, From, StateName, State0, Connection) when is_pid(Pid) ->
+ %% terminate will send close alert to peer
+ State = State0#state{downgrade = {Pid, From}},
+ Connection:terminate(downgrade, StateName, State),
+ %% User downgrades connection
+ %% When downgrading an TLS connection to a transport connection
+ %% we must recive the close alert from the peer before releasing the
+ %% transport socket.
+ {next_state, downgrade, State, [{timeout, Timeout, downgrade}]};
+handle_call({close, _} = Close, From, StateName, State, Connection) ->
%% Run terminate before returning so that the reuseaddr
- %% inet-option and possible downgrade will work as intended.
+ %% inet-option
Result = Connection:terminate(Close, StateName, State),
- {stop, normal, Result, State#state{terminated = true}};
-
-handle_sync_event({shutdown, How0}, _, StateName,
- #state{transport_cb = Transport,
- negotiated_version = Version,
- connection_states = ConnectionStates,
- socket = Socket} = State) ->
+ {stop_and_reply, {shutdown, normal},
+ {reply, From, Result}, State};
+handle_call({shutdown, How0}, From, _,
+ #state{transport_cb = Transport,
+ negotiated_version = Version,
+ connection_states = ConnectionStates,
+ socket = Socket}, _) ->
case How0 of
How when How == write; How == both ->
Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
@@ -733,95 +844,91 @@ handle_sync_event({shutdown, How0}, _, StateName,
_ ->
ok
end,
-
+
case Transport:shutdown(Socket, How0) of
ok ->
- {reply, ok, StateName, State, get_timeout(State)};
+ {keep_state_and_data, [{reply, From, ok}]};
Error ->
- {stop, normal, Error, State}
+ gen_statem:reply(From, {error, Error}),
+ {stop, normal}
end;
-handle_sync_event({recv, _N, _Timeout}, _RecvFrom, StateName,
- #state{socket_options = #socket_options{active = Active}} = State) when Active =/= false ->
- {reply, {error, einval}, StateName, State, get_timeout(State)};
-handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName,
- #state{protocol_cb = Connection} = State0) ->
- Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
- Connection:passive_receive(State0#state{bytes_to_read = N,
- start_or_recv_from = RecvFrom, timer = Timer}, StateName);
-%% Doing renegotiate wait with handling request until renegotiate is
-%% finished. Will be handled by next_state_is_connection/2.
-handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
+handle_call({recv, _N, _Timeout}, From, _,
+ #state{socket_options =
+ #socket_options{active = Active}}, _) when Active =/= false ->
+ {keep_state_and_data, [{reply, From, {error, einval}}]};
+handle_call({recv, N, Timeout}, RecvFrom, StateName, State, _) ->
+ %% Doing renegotiate wait with handling request until renegotiate is
+ %% finished.
Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
{next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom,
- timer = Timer},
- get_timeout(State)};
-handle_sync_event({new_user, User}, _From, StateName,
- State =#state{user_application = {OldMon, _}}) ->
+ timer = Timer},
+ [{next_event, internal, {recv, RecvFrom}}]};
+handle_call({new_user, User}, From, StateName,
+ State =#state{user_application = {OldMon, _}}, _) ->
NewMon = erlang:monitor(process, User),
erlang:demonitor(OldMon, [flush]),
- {reply, ok, StateName, State#state{user_application = {NewMon,User}},
- get_timeout(State)};
-handle_sync_event({get_opts, OptTags}, _From, StateName,
+ {next_state, StateName, State#state{user_application = {NewMon,User}},
+ [{reply, From, ok}]};
+handle_call({get_opts, OptTags}, From, _,
#state{socket = Socket,
transport_cb = Transport,
- socket_options = SockOpts} = State) ->
+ socket_options = SockOpts}, _) ->
OptsReply = get_socket_opts(Transport, Socket, OptTags, SockOpts, []),
- {reply, OptsReply, StateName, State, get_timeout(State)};
-handle_sync_event(negotiated_protocol, _From, StateName, #state{negotiated_protocol = undefined} = State) ->
- {reply, {error, protocol_not_negotiated}, StateName, State, get_timeout(State)};
-handle_sync_event(negotiated_protocol, _From, StateName, #state{negotiated_protocol = SelectedProtocol} = State) ->
- {reply, {ok, SelectedProtocol}, StateName, State, get_timeout(State)};
-handle_sync_event({set_opts, Opts0}, _From, StateName0,
- #state{socket_options = Opts1,
+ {keep_state_and_data, [{reply, From, OptsReply}]};
+handle_call({set_opts, Opts0}, From, connection = StateName0,
+ #state{socket_options = Opts1,
protocol_cb = Connection,
socket = Socket,
transport_cb = Transport,
- user_data_buffer = Buffer} = State0) ->
+ user_data_buffer = Buffer} = State0, _) ->
{Reply, Opts} = set_socket_opts(Transport, Socket, Opts0, Opts1, []),
State1 = State0#state{socket_options = Opts},
if
Opts#socket_options.active =:= false ->
- {reply, Reply, StateName0, State1, get_timeout(State1)};
+ hibernate_after(StateName0, State1, [{reply, From, Reply}]);
Buffer =:= <<>>, Opts1#socket_options.active =:= false ->
%% Need data, set active once
{Record, State2} = Connection:next_record_if_active(State1),
%% Note: Renogotiation may cause StateName0 =/= StateName
- case Connection:next_state(StateName0, StateName0, Record, State2) of
- {next_state, StateName, State, Timeout} ->
- {reply, Reply, StateName, State, Timeout};
+ case Connection:next_event(StateName0, Record, State2) of
+ {next_state, StateName, State} ->
+ hibernate_after(StateName, State, [{reply, From, Reply}]);
+ {next_state, StateName, State, Actions} ->
+ hibernate_after(StateName, State, [{reply, From, Reply} | Actions]);
{stop, Reason, State} ->
{stop, Reason, State}
end;
Buffer =:= <<>> ->
%% Active once already set
- {reply, Reply, StateName0, State1, get_timeout(State1)};
+ hibernate_after(StateName0, State1, [{reply, From, Reply}]);
true ->
case Connection:read_application_data(<<>>, State1) of
- Stop = {stop,_,_} ->
- Stop;
+ {stop, Reason, State} ->
+ {stop, Reason, State};
{Record, State2} ->
%% Note: Renogotiation may cause StateName0 =/= StateName
- case Connection:next_state(StateName0, StateName0, Record, State2) of
- {next_state, StateName, State, Timeout} ->
- {reply, Reply, StateName, State, Timeout};
- {stop, Reason, State} ->
- {stop, Reason, State}
+ case Connection:next_event(StateName0, Record, State2) of
+ {next_state, StateName, State} ->
+ hibernate_after(StateName, State, [{reply, From, Reply}]);
+ {next_state, StateName, State, Actions} ->
+ hibernate_after(StateName, State, [{reply, From, Reply} | Actions]);
+ {stop, _, _} = Stop ->
+ Stop
end
end
end;
-handle_sync_event(renegotiate, From, connection, #state{protocol_cb = Connection} = State) ->
- Connection:renegotiate(State#state{renegotiation = {true, From}});
-handle_sync_event(renegotiate, _, StateName, State) ->
- {reply, {error, already_renegotiating}, StateName, State, get_timeout(State)};
-handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
- #state{connection_states = ConnectionStates,
- negotiated_version = Version} = State) ->
+handle_call(renegotiate, From, StateName, _, _) when StateName =/= connection ->
+ {keep_state_and_data, [{reply, From, {error, already_renegotiating}}]};
+handle_call({prf, Secret, Label, Seed, WantedLength}, From, _,
+ #state{connection_states = ConnectionStates,
+ negotiated_version = Version}, _) ->
ConnectionState =
ssl_record:current_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{master_secret = MasterSecret,
client_random = ClientRandom,
- server_random = ServerRandom} = SecParams,
+ server_random = ServerRandom,
+ prf_algorithm = PRFAlgorithm} = SecParams,
Reply = try
SecretToUse = case Secret of
_ when is_binary(Secret) -> Secret;
@@ -832,32 +939,14 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
(client_random, Acc) -> [ClientRandom|Acc];
(server_random, Acc) -> [ServerRandom|Acc]
end, [], Seed)),
- ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength)
+ ssl_handshake:prf(Version, PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength)
catch
exit:_ -> {error, badarg};
error:Reason -> {error, Reason}
end,
- {reply, Reply, StateName, State, get_timeout(State)};
-handle_sync_event(session_info, _, StateName,
- #state{session = #session{session_id = Id,
- cipher_suite = Suite}} = State) ->
- {reply, [{session_id, Id},
- {cipher_suite, ssl_cipher:erl_suite_definition(Suite)}],
- StateName, State, get_timeout(State)};
-handle_sync_event(peer_certificate, _, StateName,
- #state{session = #session{peer_certificate = Cert}}
- = State) ->
- {reply, {ok, Cert}, StateName, State, get_timeout(State)};
-handle_sync_event(connection_information, _, StateName, State) ->
- Info = connection_info(State),
- {reply, {ok, Info}, StateName, State, get_timeout(State)}.
-
-connection_info(#state{sni_hostname = SNIHostname,
- session = #session{cipher_suite = CipherSuite},
- negotiated_version = Version, ssl_options = Opts}) ->
- [{protocol, tls_record:protocol_version(Version)},
- {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)},
- {sni_hostname, SNIHostname}] ++ ssl_options_list(Opts).
+ {keep_state_and_data, [{reply, From, Reply}]};
+handle_call(_,_,_,_,_) ->
+ {keep_state_and_data, [postpone]}.
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
@@ -865,7 +954,8 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName,
protocol_cb = Connection,
error_tag = ErrorTag,
tracker = Tracker} = State) when StateName =/= connection ->
- Connection:alert_user(Transport, Tracker,Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
+ Connection:alert_user(Transport, Tracker,Socket,
+ StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
@@ -888,47 +978,46 @@ handle_info({'EXIT', Socket, normal}, _StateName, #state{socket = Socket} = Stat
{stop, {shutdown, transport_closed}, State};
handle_info(allow_renegotiate, StateName, State) ->
- {next_state, StateName, State#state{allow_renegotiate = true}, get_timeout(State)};
+ {next_state, StateName, State#state{allow_renegotiate = true}};
handle_info({cancel_start_or_recv, StartFrom}, StateName,
#state{renegotiation = {false, first}} = State) when StateName =/= connection ->
- gen_fsm:reply(StartFrom, {error, timeout}),
- {stop, {shutdown, user_timeout}, State#state{timer = undefined}};
+ {stop_and_reply, {shutdown, user_timeout},
+ {reply, StartFrom, {error, timeout}}, State#state{timer = undefined}};
-handle_info({cancel_start_or_recv, RecvFrom}, StateName, #state{start_or_recv_from = RecvFrom} = State) ->
- gen_fsm:reply(RecvFrom, {error, timeout}),
+handle_info({cancel_start_or_recv, RecvFrom}, StateName,
+ #state{start_or_recv_from = RecvFrom} = State) when RecvFrom =/= undefined ->
{next_state, StateName, State#state{start_or_recv_from = undefined,
bytes_to_read = undefined,
- timer = undefined}, get_timeout(State)};
+ timer = undefined}, [{reply, RecvFrom, {error, timeout}}]};
handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) ->
- {next_state, StateName, State#state{timer = undefined}, get_timeout(State)};
+ {next_state, StateName, State#state{timer = undefined}};
handle_info(Msg, StateName, #state{socket = Socket, error_tag = Tag} = State) ->
Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [{Msg, Tag, Socket}]),
error_logger:info_report(Report),
- {next_state, StateName, State, get_timeout(State)}.
-
+ {next_state, StateName, State}.
+%%--------------------------------------------------------------------
+%% gen_statem callbacks
+%%--------------------------------------------------------------------
terminate(_, _, #state{terminated = true}) ->
%% Happens when user closes the connection using ssl:close/1
%% we want to guarantee that Transport:close has been called
%% when ssl:close/1 returns.
ok;
+
terminate({shutdown, transport_closed} = Reason,
- _StateName, #state{send_queue = SendQueue, protocol_cb = Connection,
- socket = Socket, transport_cb = Transport,
- renegotiation = Renegotiate} = State) ->
+ _StateName, #state{protocol_cb = Connection,
+ socket = Socket, transport_cb = Transport} = State) ->
handle_trusted_certs_db(State),
- notify_senders(SendQueue),
- notify_renegotiater(Renegotiate),
Connection:close(Reason, Socket, Transport, undefined, undefined);
-terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue, protocol_cb = Connection,
- socket = Socket, transport_cb = Transport,
- renegotiation = Renegotiate} = State) ->
+terminate({shutdown, own_alert}, _StateName, #state{%%send_queue = SendQueue,
+ protocol_cb = Connection,
+ socket = Socket,
+ transport_cb = Transport} = State) ->
handle_trusted_certs_db(State),
- notify_senders(SendQueue),
- notify_renegotiater(Renegotiate),
case application:get_env(ssl, alert_timeout) of
{ok, Timeout} when is_integer(Timeout) ->
Connection:close({timeout, Timeout}, Socket, Transport, undefined, undefined);
@@ -939,26 +1028,22 @@ terminate(Reason, connection, #state{negotiated_version = Version,
protocol_cb = Connection,
connection_states = ConnectionStates0,
ssl_options = #ssl_options{padding_check = Check},
- transport_cb = Transport, socket = Socket,
- send_queue = SendQueue, renegotiation = Renegotiate} = State) ->
+ transport_cb = Transport, socket = Socket
+ } = State) ->
handle_trusted_certs_db(State),
- notify_senders(SendQueue),
- notify_renegotiater(Renegotiate),
{BinAlert, ConnectionStates} = terminate_alert(Reason, Version, ConnectionStates0),
Transport:send(Socket, BinAlert),
Connection:close(Reason, Socket, Transport, ConnectionStates, Check);
terminate(Reason, _StateName, #state{transport_cb = Transport, protocol_cb = Connection,
- socket = Socket, send_queue = SendQueue,
- renegotiation = Renegotiate} = State) ->
+ socket = Socket
+ } = State) ->
handle_trusted_certs_db(State),
- notify_senders(SendQueue),
- notify_renegotiater(Renegotiate),
Connection:close(Reason, Socket, Transport, undefined, undefined).
-format_status(normal, [_, State]) ->
- [{data, [{"StateData", State}]}];
-format_status(terminate, [_, State]) ->
+format_status(normal, [_, StateName, State]) ->
+ [{data, [{"State", {StateName, State}}]}];
+format_status(terminate, [_, StateName, State]) ->
SslOptions = (State#state.ssl_options),
NewOptions = SslOptions#ssl_options{password = ?SECRET_PRINTOUT,
cert = ?SECRET_PRINTOUT,
@@ -967,39 +1052,29 @@ format_status(terminate, [_, State]) ->
dh = ?SECRET_PRINTOUT,
psk_identity = ?SECRET_PRINTOUT,
srp_identity = ?SECRET_PRINTOUT},
- [{data, [{"StateData", State#state{connection_states = ?SECRET_PRINTOUT,
- protocol_buffers = ?SECRET_PRINTOUT,
- user_data_buffer = ?SECRET_PRINTOUT,
- tls_handshake_history = ?SECRET_PRINTOUT,
- session = ?SECRET_PRINTOUT,
- private_key = ?SECRET_PRINTOUT,
- diffie_hellman_params = ?SECRET_PRINTOUT,
- diffie_hellman_keys = ?SECRET_PRINTOUT,
- srp_params = ?SECRET_PRINTOUT,
- srp_keys = ?SECRET_PRINTOUT,
- premaster_secret = ?SECRET_PRINTOUT,
- ssl_options = NewOptions
- }}]}].
+ [{data, [{"State", {StateName, State#state{connection_states = ?SECRET_PRINTOUT,
+ protocol_buffers = ?SECRET_PRINTOUT,
+ user_data_buffer = ?SECRET_PRINTOUT,
+ tls_handshake_history = ?SECRET_PRINTOUT,
+ session = ?SECRET_PRINTOUT,
+ private_key = ?SECRET_PRINTOUT,
+ diffie_hellman_params = ?SECRET_PRINTOUT,
+ diffie_hellman_keys = ?SECRET_PRINTOUT,
+ srp_params = ?SECRET_PRINTOUT,
+ srp_keys = ?SECRET_PRINTOUT,
+ premaster_secret = ?SECRET_PRINTOUT,
+ ssl_options = NewOptions}
+ }}]}].
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-ssl_config(Opts, Role, State) ->
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} =
- ssl_config:init(Opts, Role),
- Handshake = ssl_handshake:init_handshake_history(),
- TimeStamp = erlang:monotonic_time(),
- Session = State#state.session,
- State#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,
- crl_db = CRLDbInfo,
- session_cache = CacheHandle,
- private_key = Key,
- diffie_hellman_params = DHParams,
- ssl_options = Opts}.
+connection_info(#state{sni_hostname = SNIHostname,
+ session = #session{cipher_suite = CipherSuite},
+ negotiated_version = Version, ssl_options = Opts}) ->
+ [{protocol, tls_record:protocol_version(Version)},
+ {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)},
+ {sni_hostname, SNIHostname}] ++ ssl_options_list(Opts).
do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} =
ServerHelloExt,
@@ -1033,7 +1108,7 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite,
cipher_suite = CipherSuite,
compression_method = Compression},
{Record, State} = Connection:next_record(State2#state{session = Session}),
- Connection:next_state(hello, certify, Record, State)
+ Connection:next_event(certify, Record, State)
catch
#alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, hello, State0)
@@ -1051,7 +1126,7 @@ resumed_server_hello(#state{session = Session,
State2 =
finalize_handshake(State1, abbreviated, Connection),
{Record, State} = Connection:next_record(State2),
- Connection:next_state(hello, abbreviated, Record, State);
+ Connection:next_event(abbreviated, Record, State);
#alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, hello, State0)
end.
@@ -1076,7 +1151,7 @@ handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State2 = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlg, State1),
{Record, State} = Connection:next_record(State2),
- Connection:next_state(certify, certify, Record, State).
+ Connection:next_event(certify, Record, State).
handle_peer_cert_key(client, _,
{?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey,
@@ -1139,7 +1214,7 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
%% Reinitialize
client_certificate_requested = false},
{Record, State} = Connection:next_record(State3),
- Connection:next_state(certify, cipher, Record, State)
+ Connection:next_event(cipher, Record, State)
catch
throw:#alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, certify, State0)
@@ -1173,20 +1248,25 @@ certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientP
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_psk_identity{} = ClientKey,
- #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection) ->
+ #state{ssl_options =
+ #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
#state{diffie_hellman_params = #'DHParameter'{} = Params,
diffie_hellman_keys = {_, ServerDhPrivateKey},
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ ssl_options =
+ #ssl_options{user_lookup_fun = PSKLookup}} = State0,
Connection) ->
- PremasterSecret = ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup),
+ PremasterSecret =
+ ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey,
#state{private_key = Key,
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ ssl_options =
+ #ssl_options{user_lookup_fun = PSKLookup}} = State0,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
@@ -1198,8 +1278,11 @@ certify_client_key_exchange(#client_srp_public{} = ClientKey,
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, Params),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher).
-certify_server(#state{key_algorithm = Algo} = State, _)
- when Algo == dh_anon; Algo == ecdh_anon; Algo == psk; Algo == dhe_psk; Algo == srp_anon ->
+certify_server(#state{key_algorithm = Algo} = State, _) when Algo == dh_anon;
+ Algo == ecdh_anon;
+ Algo == psk;
+ Algo == dhe_psk;
+ Algo == srp_anon ->
State;
certify_server(#state{cert_db = CertDbHandle,
@@ -1297,10 +1380,11 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk,
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, DHKeys, Params,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk,
+ PskIdentityHint, DHKeys, Params,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
State = Connection:send_handshake(Msg, State0),
State#state{diffie_hellman_keys = DHKeys};
@@ -1389,7 +1473,8 @@ key_exchange(#state{role = client,
ssl_options = SslOpts,
key_algorithm = psk,
negotiated_version = Version} = State0, Connection) ->
- Msg = ssl_handshake:key_exchange(client, Version, {psk, SslOpts#ssl_options.psk_identity}),
+ Msg = ssl_handshake:key_exchange(client, Version,
+ {psk, SslOpts#ssl_options.psk_identity}),
Connection:send_handshake(Msg, State0);
key_exchange(#state{role = client,
@@ -1398,7 +1483,8 @@ key_exchange(#state{role = client,
negotiated_version = Version,
diffie_hellman_keys = {DhPubKey, _}} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, Version,
- {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}),
+ {dhe_psk,
+ SslOpts#ssl_options.psk_identity, DhPubKey}),
Connection:send_handshake(Msg, State0);
key_exchange(#state{role = client,
ssl_options = SslOpts,
@@ -1438,7 +1524,8 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
rsa_key_exchange(_, _, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
-rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
+rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret,
+ PublicKeyInfo = {Algorithm, _, _})
when Algorithm == ?rsaEncryption;
Algorithm == ?md2WithRSAEncryption;
Algorithm == ?md5WithRSAEncryption;
@@ -1473,10 +1560,11 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
State, _) ->
State.
-calculate_master_secret(PremasterSecret, #state{negotiated_version = Version,
- connection_states = ConnectionStates0,
- session = Session0} = State0, Connection,
- Current, Next) ->
+calculate_master_secret(PremasterSecret,
+ #state{negotiated_version = Version,
+ connection_states = ConnectionStates0,
+ session = Session0} = State0, Connection,
+ _Current, Next) ->
case ssl_handshake:master_secret(record_cb(Connection), Version, PremasterSecret,
ConnectionStates0, server) of
{MasterSecret, ConnectionStates} ->
@@ -1484,7 +1572,7 @@ calculate_master_secret(PremasterSecret, #state{negotiated_version = Version,
State1 = State0#state{connection_states = ConnectionStates,
session = Session},
{Record, State} = Connection:next_record(State1),
- Connection:next_state(Current, Next, Record, State);
+ Connection:next_event(Next, Record, State);
#alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, certify, State0)
end.
@@ -1535,31 +1623,36 @@ save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbrev
save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) ->
ssl_record:set_server_verify_data(current_write, Data, ConnectionStates).
-calculate_secret(#server_dh_params{dh_p = Prime, dh_g = Base, dh_y = ServerPublicDhKey} = Params,
- State, Connection) ->
+calculate_secret(#server_dh_params{dh_p = Prime, dh_g = Base,
+ dh_y = ServerPublicDhKey} = Params,
+ State, Connection) ->
Keys = {_, PrivateDhKey} = crypto:generate_key(dh, [Prime, Base]),
PremasterSecret =
ssl_handshake:premaster_secret(ServerPublicDhKey, PrivateDhKey, Params),
calculate_master_secret(PremasterSecret,
- State#state{diffie_hellman_keys = Keys}, Connection, certify, certify);
+ State#state{diffie_hellman_keys = Keys},
+ Connection, certify, certify);
calculate_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey},
State, Connection) ->
ECDHKeys = public_key:generate_key(ECCurve),
- PremasterSecret = ssl_handshake:premaster_secret(#'ECPoint'{point = ECServerPubKey}, ECDHKeys),
+ PremasterSecret =
+ ssl_handshake:premaster_secret(#'ECPoint'{point = ECServerPubKey}, ECDHKeys),
calculate_master_secret(PremasterSecret,
- State#state{diffie_hellman_keys = ECDHKeys}, Connection, certify, certify);
+ State#state{diffie_hellman_keys = ECDHKeys},
+ Connection, certify, certify);
calculate_secret(#server_psk_params{
- hint = IdentityHint},
+ hint = IdentityHint},
State0, Connection) ->
%% store for later use
{Record, State} = Connection:next_record(State0#state{psk_identity = IdentityHint}),
- Connection:next_state(certify, certify, Record, State);
+ Connection:next_event(certify, Record, State);
calculate_secret(#server_dhe_psk_params{
dh_params = #server_dh_params{dh_p = Prime, dh_g = Base}} = ServerKey,
- #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State, Connection) ->
+ #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
+ State, Connection) ->
Keys = {_, PrivateDhKey} =
crypto:generate_key(dh, [Prime, Base]),
PremasterSecret = ssl_handshake:premaster_secret(ServerKey, PrivateDhKey, PSKLookup),
@@ -1567,10 +1660,12 @@ calculate_secret(#server_dhe_psk_params{
Connection, certify, certify);
calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKey,
- #state{ssl_options = #ssl_options{srp_identity = SRPId}} = State, Connection) ->
+ #state{ssl_options = #ssl_options{srp_identity = SRPId}} = State,
+ Connection) ->
Keys = generate_srp_client_keys(Generator, Prime, 0),
PremasterSecret = ssl_handshake:premaster_secret(ServerKey, Keys, SRPId),
- calculate_master_secret(PremasterSecret, State#state{srp_keys = Keys}, Connection, certify, certify).
+ calculate_master_secret(PremasterSecret, State#state{srp_keys = Keys}, Connection,
+ certify, certify).
master_secret(#alert{} = Alert, _) ->
Alert;
@@ -1626,21 +1721,23 @@ handle_srp_identity(Username, {Fun, UserState}) ->
end.
-cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State,
+cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State0,
Connection) ->
- ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0),
- Connection:next_state_connection(cipher,
- ack_connection(
- State#state{session = Session,
- connection_states = ConnectionStates}));
-
+ ConnectionStates = ssl_record:set_server_verify_data(current_both, Data,
+ ConnectionStates0),
+ {Record, State} = prepare_connection(State0#state{session = Session,
+ connection_states = ConnectionStates},
+ Connection),
+ Connection:next_event(connection, Record, State);
cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State0,
Connection) ->
- ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
- State =
+ ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data,
+ ConnectionStates0),
+ State1 =
finalize_handshake(State0#state{connection_states = ConnectionStates1,
session = Session}, cipher, Connection),
- Connection:next_state_connection(cipher, ack_connection(State#state{session = Session})).
+ {Record, State} = prepare_connection(State1, Connection),
+ Connection:next_event(connection, Record, State).
select_curve(#state{client_ecc = {[Curve|_], _}}) ->
{namedCurve, Curve};
@@ -1674,8 +1771,8 @@ record_cb(tls_connection) ->
record_cb(dtls_connection) ->
dtls_record.
-sync_send_all_state_event(FsmPid, Event) ->
- try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity)
+call(FsmPid, Event) ->
+ try gen_statem:call(FsmPid, Event)
catch
exit:{noproc, _} ->
{error, closed};
@@ -1731,38 +1828,42 @@ set_socket_opts(Transport, Socket, [], SockOpts, Other) ->
{{error, {options, {socket_options, Other, Error}}}, SockOpts}
end;
-set_socket_opts(Transport,Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary ->
+set_socket_opts(Transport,Socket, [{mode, Mode}| Opts], SockOpts, Other)
+ when Mode == list; Mode == binary ->
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{mode = Mode}, Other);
set_socket_opts(_, _, [{mode, _} = Opt| _], SockOpts, _) ->
{{error, {options, {socket_options, Opt}}}, SockOpts};
-set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw;
- Packet == 0;
- Packet == 1;
- Packet == 2;
- Packet == 4;
- Packet == asn1;
- Packet == cdr;
- Packet == sunrm;
- Packet == fcgi;
- Packet == tpkt;
- Packet == line;
- Packet == http;
- Packet == httph;
- Packet == http_bin;
- Packet == httph_bin ->
+set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other)
+ when Packet == raw;
+ Packet == 0;
+ Packet == 1;
+ Packet == 2;
+ Packet == 4;
+ Packet == asn1;
+ Packet == cdr;
+ Packet == sunrm;
+ Packet == fcgi;
+ Packet == tpkt;
+ Packet == line;
+ Packet == http;
+ Packet == httph;
+ Packet == http_bin;
+ Packet == httph_bin ->
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{packet = Packet}, Other);
set_socket_opts(_, _, [{packet, _} = Opt| _], SockOpts, _) ->
{{error, {options, {socket_options, Opt}}}, SockOpts};
-set_socket_opts(Transport, Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) ->
+set_socket_opts(Transport, Socket, [{header, Header}| Opts], SockOpts, Other)
+ when is_integer(Header) ->
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{header = Header}, Other);
set_socket_opts(_, _, [{header, _} = Opt| _], SockOpts, _) ->
{{error,{options, {socket_options, Opt}}}, SockOpts};
-set_socket_opts(Transport, Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once;
- Active == true;
- Active == false ->
+set_socket_opts(Transport, Socket, [{active, Active}| Opts], SockOpts, Other)
+ when Active == once;
+ Active == true;
+ Active == false ->
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{active = Active}, Other);
set_socket_opts(_, _, [{active, _} = Opt| _], SockOpts, _) ->
@@ -1775,11 +1876,10 @@ start_or_recv_cancel_timer(infinity, _RecvFrom) ->
start_or_recv_cancel_timer(Timeout, RecvFrom) ->
erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
-get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) ->
- infinity;
-get_timeout(#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}}) ->
- HibernateAfter.
-
+hibernate_after(StateName, #state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}} = State,
+ Actions) ->
+ {next_state, StateName, State, [{timeout, HibernateAfter, hibernate} | Actions]}.
+
terminate_alert(normal, Version, ConnectionStates) ->
ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
Version, ConnectionStates);
@@ -1789,10 +1889,12 @@ terminate_alert({Reason, _}, Version, ConnectionStates) when Reason == close;
Version, ConnectionStates);
terminate_alert(_, Version, ConnectionStates) ->
- ssl_alert:encode(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
- Version, ConnectionStates).
+ {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
+ Version, ConnectionStates),
+ BinAlert.
-handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cacerts = []}}) ->
+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,
@@ -1802,7 +1904,8 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref,
%% with other connections and it is safe to delete them when the connection ends.
ssl_pkix_db:remove_trusted_certs(Ref, CertDb);
handle_trusted_certs_db(#state{file_ref_db = undefined}) ->
- %% Something went wrong early (typically cacertfile does not exist) so there is nothing to handle
+ %% Something went wrong early (typically cacertfile does not
+ %% exist) so there is nothing to handle
ok;
handle_trusted_certs_db(#state{cert_db_ref = Ref,
file_ref_db = RefDb,
@@ -1814,29 +1917,31 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref,
ok
end.
-notify_senders(SendQueue) ->
- lists:foreach(fun({From, _}) ->
- gen_fsm:reply(From, {error, closed})
- end, queue:to_list(SendQueue)).
-
-notify_renegotiater({true, From}) when not is_atom(From) ->
- gen_fsm:reply(From, {error, closed});
-notify_renegotiater(_) ->
- ok.
+prepare_connection(#state{renegotiation = Renegotiate,
+ start_or_recv_from = RecvFrom} = State0, Connection)
+ when Renegotiate =/= {false, first},
+ RecvFrom =/= undefined ->
+ State1 = Connection:reinit_handshake_data(State0),
+ {Record, State} = Connection:next_record(State1),
+ {Record, ack_connection(State)};
+prepare_connection(State0, Connection) ->
+ State = Connection:reinit_handshake_data(State0),
+ {no_record, ack_connection(State)}.
ack_connection(#state{renegotiation = {true, Initiater}} = State)
when Initiater == internal;
Initiater == peer ->
State#state{renegotiation = undefined};
ack_connection(#state{renegotiation = {true, From}} = State) ->
- gen_fsm:reply(From, ok),
+ gen_statem:reply(From, ok),
State#state{renegotiation = undefined};
ack_connection(#state{renegotiation = {false, first},
start_or_recv_from = StartFrom,
timer = Timer} = State) when StartFrom =/= undefined ->
- gen_fsm:reply(StartFrom, connected),
+ gen_statem:reply(StartFrom, connected),
cancel_timer(Timer),
- State#state{renegotiation = undefined, start_or_recv_from = undefined, timer = undefined};
+ State#state{renegotiation = undefined,
+ start_or_recv_from = undefined, timer = undefined};
ack_connection(State) ->
State.
@@ -1857,13 +1962,14 @@ register_session(server, _, Port, #session{is_resumable = new} = Session0) ->
register_session(_, _, _, Session) ->
Session. %% Already registered
-handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0,
- protocol_cb = Connection} = State0) ->
+handle_new_session(NewId, CipherSuite, Compression,
+ #state{session = Session0,
+ protocol_cb = Connection} = State0) ->
Session = Session0#session{session_id = NewId,
cipher_suite = CipherSuite,
compression_method = Compression},
{Record, State} = Connection:next_record(State0#state{session = Session}),
- Connection:next_state(hello, certify, Record, State).
+ Connection:next_event(certify, Record, State).
handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
negotiated_version = Version,
@@ -1879,13 +1985,13 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
Connection:next_record(State0#state{
connection_states = ConnectionStates,
session = Session}),
- Connection:next_state(hello, abbreviated, Record, State);
+ Connection:next_event(abbreviated, Record, State);
#alert{} = Alert ->
Connection:handle_own_alert(Alert, Version, hello, State0)
end.
make_premaster_secret({MajVer, MinVer}, rsa) ->
- Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
+ Rand = ssl_cipher:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
make_premaster_secret(_, _) ->
undefined.
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index bb41ef2b62..7682cb86ea 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -75,7 +75,7 @@
renegotiation :: undefined | {boolean(), From::term() | internal | peer},
start_or_recv_from :: term(),
timer :: undefined | reference(), % start_or_recive_timer
- send_queue :: queue:queue(),
+ %%send_queue :: queue:queue(),
terminated = false ::boolean(),
allow_renegotiate = true ::boolean(),
expecting_next_protocol_negotiation = false ::boolean(),
@@ -83,7 +83,8 @@
negotiated_protocol = undefined :: undefined | binary(),
client_ecc, % {Curves, PointFmt}
tracker :: pid() | 'undefined', %% Tracker process for listen socket
- sni_hostname = undefined
+ sni_hostname = undefined,
+ downgrade
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 2a2a7b7d25..598d4e4112 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -74,7 +74,7 @@
]).
%% MISC
--export([select_version/3, prf/5, select_hashsign/5,
+-export([select_version/3, prf/6, select_hashsign/5,
select_hashsign_algs/3,
premaster_secret/2, premaster_secret/3, premaster_secret/4]).
@@ -564,17 +564,15 @@ server_key_exchange_hash(md5sha, Value) ->
server_key_exchange_hash(Hash, Value) ->
crypto:hash(Hash, Value).
%%--------------------------------------------------------------------
--spec prf(ssl_record:ssl_version(), binary(), binary(), [binary()], non_neg_integer()) ->
+-spec prf(ssl_record:ssl_version(), non_neg_integer(), binary(), binary(), [binary()], non_neg_integer()) ->
{ok, binary()} | {error, undefined}.
%%
%% Description: use the TLS PRF to generate key material
%%--------------------------------------------------------------------
-prf({3,0}, _, _, _, _) ->
+prf({3,0}, _, _, _, _, _) ->
{error, undefined};
-prf({3,1}, Secret, Label, Seed, WantedLength) ->
- {ok, tls_v1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
-prf({3,_N}, Secret, Label, Seed, WantedLength) ->
- {ok, tls_v1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
+prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
+ {ok, tls_v1:prf(PRFAlgo, Secret, Label, Seed, WantedLength)}.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 9c52f5a315..076e663cd4 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -118,7 +118,7 @@
%% undefined if not hibernating, or number of ms of
%% inactivity after which ssl_connection will go into
%% hibernation
- hibernate_after :: boolean() | 'undefined',
+ hibernate_after :: timeout(),
%% This option should only be set to true by inet_tls_dist
erl_dist = false :: boolean(),
alpn_advertised_protocols = undefined :: [binary()] | undefined ,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index e273581de9..60b4fbe995 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -551,7 +551,7 @@ last_delay_timer({_,_}, TRef, {_, LastClient}) ->
new_id(_, 0, _, _) ->
<<>>;
new_id(Port, Tries, Cache, CacheCb) ->
- Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES),
+ Id = ssl_cipher:random_bytes(?NUM_OF_SESSION_ID_BYTES),
case CacheCb:lookup(Cache, {Port, Id}) of
undefined ->
Now = erlang:monotonic_time(),
@@ -610,8 +610,8 @@ server_register_session(Port, Session, #state{session_cache_server_max = Max,
do_register_session(Key, Session, Max, Pid, Cache, CacheCb) ->
try CacheCb:size(Cache) of
- N when N > Max ->
- invalidate_session_cache(Pid, CacheCb, Cache);
+ Max ->
+ invalidate_session_cache(Pid, CacheCb, Cache);
_ ->
CacheCb:update(Cache, Key, Session),
Pid
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index ecff950668..866bfcef7e 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -460,7 +460,7 @@ empty_security_params(ConnectionEnd = ?SERVER) ->
random() ->
Secs_since_1970 = calendar:datetime_to_gregorian_seconds(
calendar:universal_time()) - 62167219200,
- Random_28_bytes = crypto:rand_bytes(28),
+ Random_28_bytes = ssl_cipher:random_bytes(28),
<<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index a1d13d1850..208edc644a 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -28,7 +28,7 @@
-module(tls_connection).
--behaviour(gen_fsm).
+-behaviour(gen_statem).
-include("tls_connection.hrl").
-include("tls_handshake.hrl").
@@ -43,31 +43,33 @@
%% Internal application API
%% Setup
--export([start_fsm/8]).
+-export([start_fsm/8, start_link/7, init/1]).
%% State transition handling
--export([next_record/1, next_state/4, next_state_connection/2]).
+-export([next_record/1, next_event/3]).
%% Handshake handling
--export([renegotiate/1, send_handshake/2, send_change_cipher/2]).
+-export([renegotiate/2, send_handshake/2, send_change_cipher/2,
+ reinit_handshake_data/1]).
%% Alert and close handling
-export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
- handle_normal_shutdown/3, handle_unexpected_message/3,
+ handle_normal_shutdown/3,
close/5, alert_user/6, alert_user/9
]).
%% Data handling
-export([write_application_data/3, read_application_data/2,
- passive_receive/2, next_record_if_active/1]).
+ passive_receive/2, next_record_if_active/1, handle_common_event/4]).
-%% 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, format_status/2]).
+%% gen_statem state functions
+-export([init/3, error/3, downgrade/3, %% Initiation and take down states
+ hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
+ connection/3]).
+%% gen_statem callbacks
+-export([terminate/3, code_change/4, format_status/2]).
+
+-define(GEN_STATEM_CB_MODE, state_functions).
%%====================================================================
%% Internal application API
@@ -130,6 +132,16 @@ send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
Transport:send(Socket, BinChangeCipher),
State0#state{connection_states = ConnectionStates}.
+reinit_handshake_data(State) ->
+ %% premaster_secret, public_key_info and tls_handshake_info
+ %% are only needed during the handshake phase.
+ %% To reduce memory foot print of a connection reinitialize them.
+ State#state{
+ premaster_secret = undefined,
+ public_key_info = undefined,
+ tls_handshake_history = ssl_handshake:init_handshake_history()
+ }.
+
%%====================================================================
%% tls_connection_sup API
%%====================================================================
@@ -147,23 +159,34 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
process_flag(trap_exit, true),
- State = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State)).
+ State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ try
+ State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
+ gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, init, State)
+ catch throw:Error ->
+ gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error, 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) ->
+%% State functions
+%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+-spec init(gen_statem:event_type(),
+ {start, timeout()} | term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+
+init({call, From}, {start, Timeout},
+ #state{host = Host, port = Port, role = client,
+ ssl_options = SslOpts,
+ session = #session{own_certificate = Cert} = Session0,
+ transport_cb = Transport, socket = Socket,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb
+ } = State0) ->
+ Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From),
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
@@ -177,13 +200,36 @@ hello(start, #state{host = Host, port = Port, role = client,
negotiated_version = Version, %% Requested version
session =
Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_history = Handshake},
+ tls_handshake_history = Handshake,
+ start_or_recv_from = From,
+ timer = Timer},
{Record, State} = next_record(State1),
- next_state(hello, hello, Record, State);
+ next_event(hello, Record, State);
+init(Type, Event, State) ->
+ ssl_connection:init(Type, Event, State, ?MODULE).
+
+%%--------------------------------------------------------------------
+-spec error(gen_statem:event_type(),
+ {start, timeout()} | term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
-hello(Hello = #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves}},
+error({call, From}, {start, _Timeout}, {Error, State}) ->
+ {stop_and_reply, normal, {reply, From, {error, Error}}, State};
+error({call, From}, Msg, State) ->
+ handle_call(Msg, From, error, State);
+error(_, _, _) ->
+ {keep_state_and_data, [postpone]}.
+
+%%--------------------------------------------------------------------
+-spec hello(gen_statem:event_type(),
+ #hello_request{} | #client_hello{} | #server_hello{} | term(),
+ #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+hello(internal, #client_hello{client_version = ClientVersion,
+ extensions = #hello_extensions{ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves}} = Hello,
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -203,7 +249,8 @@ hello(Hello = #client_hello{client_version = ClientVersion,
undefined -> CurrentProtocol;
_ -> Protocol0
end,
- ssl_connection:hello({common_client_hello, Type, ServerHelloExt},
+
+ ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
hashsign_algorithm = HashSign,
@@ -211,8 +258,7 @@ hello(Hello = #client_hello{client_version = ClientVersion,
client_ecc = {EllipticCurves, EcPointFormats},
negotiated_protocol = Protocol}, ?MODULE)
end;
-
-hello(Hello = #server_hello{},
+hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
role = client,
@@ -225,25 +271,52 @@ hello(Hello = #server_hello{},
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
end;
+hello(info, Event, State) ->
+ handle_info(Event, hello, State);
+hello(Type, Event, State) ->
+ ssl_connection:hello(Type, Event, State, ?MODULE).
-hello(Msg, State) ->
- ssl_connection:hello(Msg, State, ?MODULE).
-
-abbreviated(Msg, State) ->
- ssl_connection:abbreviated(Msg, State, ?MODULE).
+%%--------------------------------------------------------------------
+-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+abbreviated(info, Event, State) ->
+ handle_info(Event, abbreviated, State);
+abbreviated(Type, Event, State) ->
+ ssl_connection:abbreviated(Type, Event, State, ?MODULE).
-certify(Msg, State) ->
- ssl_connection:certify(Msg, State, ?MODULE).
+%%--------------------------------------------------------------------
+-spec certify(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+certify(info, Event, State) ->
+ handle_info(Event, certify, State);
+certify(Type, Event, State) ->
+ ssl_connection:certify(Type, Event, State, ?MODULE).
-cipher(Msg, State) ->
- ssl_connection:cipher(Msg, State, ?MODULE).
+%%--------------------------------------------------------------------
+-spec cipher(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+cipher(info, Event, State) ->
+ handle_info(Event, cipher, State);
+cipher(Type, Event, State) ->
+ ssl_connection:cipher(Type, Event, 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) ->
+%%--------------------------------------------------------------------
+-spec connection(gen_statem:event_type(),
+ #hello_request{} | #client_hello{}| term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+connection(info, Event, State) ->
+ handle_info(Event, connection, State);
+connection(internal, #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 = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
State1 = send_handshake(Hello, State0),
@@ -251,58 +324,49 @@ connection(#hello_request{}, #state{host = Host, port = Port,
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) ->
+ next_event(hello, Record, State);
+connection(internal, #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) ->
+ {next_state, hello, State#state{allow_renegotiate = false}, [{next_event, internal, Hello}]};
+connection(internal, #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).
+ State1 = send_alert(Alert, State0),
+ {Record, State} = ssl_connection:prepare_connection(State1, ?MODULE),
+ next_event(connection, Record, State);
+connection(Type, Event, State) ->
+ ssl_connection:connection(Type, Event, State, ?MODULE).
%%--------------------------------------------------------------------
-%% 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!
+-spec downgrade(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
%%--------------------------------------------------------------------
-handle_event(_Event, StateName, State) ->
- {next_state, StateName, State, get_timeout(State)}.
+downgrade(Type, Event, State) ->
+ ssl_connection:downgrade(Type, Event, State, ?MODULE).
%%--------------------------------------------------------------------
-%% 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.
+%% Event handling functions called by state functions to handle
+%% common or unexpected events for the state.
%%--------------------------------------------------------------------
-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).
-%%--------------------------------------------------------------------
-
+handle_call(Event, From, StateName, State) ->
+ ssl_connection:handle_call(Event, From, StateName, State, ?MODULE).
+
%% raw data from socket, unpack records
handle_info({Protocol, _, Data}, StateName,
#state{data_tag = Protocol} = State0) ->
case next_tls_record(Data, State0) of
{Record, State} ->
- next_state(StateName, StateName, Record, State);
+ next_event(StateName, Record, State);
#alert{} = Alert ->
handle_normal_shutdown(Alert, StateName, State0),
- {stop, {shutdown, own_alert}, State0}
+ {stop, {shutdown, own_alert}}
end;
-
handle_info({CloseTag, Socket}, StateName,
#state{socket = Socket, close_tag = CloseTag,
negotiated_version = Version} = State) ->
@@ -321,32 +385,98 @@ handle_info({CloseTag, Socket}, StateName,
ok
end,
handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, {shutdown, transport_closed}, State};
-
+ {stop, {shutdown, transport_closed}};
handle_info(Msg, StateName, State) ->
ssl_connection:handle_info(Msg, StateName, State).
+handle_common_event(internal, #alert{} = Alert, StateName,
+ #state{negotiated_version = Version} = State) ->
+ handle_own_alert(Alert, Version, StateName, State);
+
+%%% TLS record protocol level handshake messages
+handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
+ StateName, #state{protocol_buffers =
+ #protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
+ negotiated_version = Version} = State0) ->
+
+ Handle =
+ fun({#hello_request{} = Packet, _}, {connection, HState}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Starts new handshake (renegotiation)
+ Hs0 = ssl_handshake:init_handshake_history(),
+ {HState#state{tls_handshake_history = Hs0,
+ renegotiation = {true, peer}},
+ {next_event, internal, Packet}};
+ ({#hello_request{}, _}, {next_state, _SName, HState}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Already in negotiation so it will be ignored!
+ {HState, []};
+ ({#client_hello{} = Packet, Raw}, {connection, HState0}) ->
+ HState = handle_sni_extension(Packet, HState0),
+ Version = Packet#client_hello.client_version,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ {HState#state{tls_handshake_history = Hs1,
+ renegotiation = {true, peer}},
+ {next_event, internal, Packet}};
+
+ ({Packet, Raw}, {_SName, HState0 = #state{tls_handshake_history=Hs0}}) ->
+ HState = handle_sni_extension(Packet, HState0),
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ {HState#state{tls_handshake_history=Hs1}, {next_event, internal, Packet}}
+ end,
+ try
+ {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0),
+ State1 = State0#state{protocol_buffers =
+ Buffers#protocol_buffers{tls_packets = Packets,
+ tls_handshake_buffer = Buf}},
+ {State, Events} = tls_handshake_events(Handle, StateName, State1, []),
+ case StateName of
+ connection ->
+ ssl_connection:hibernate_after(StateName, State, Events);
+ _ ->
+ {next_state, StateName, State, Events}
+ end
+ catch throw:#alert{} = Alert ->
+ handle_own_alert(Alert, Version, StateName, State0)
+ end;
+%%% TLS record protocol level application data messages
+handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]};
+%%% TLS record protocol level change cipher messages
+handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
+%%% TLS record protocol level Alert messages
+handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
+ #state{negotiated_version = Version} = State) ->
+ case decode_alerts(EncAlerts) of
+ Alerts = [_|_] ->
+ handle_alerts(Alerts, {next_state, StateName, State});
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, StateName, State)
+ end;
+%% Ignore unknown TLS record level protocol messages
+handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
+ {next_state, 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.
+%% gen_statem callbacks
%%--------------------------------------------------------------------
terminate(Reason, StateName, State) ->
catch ssl_connection:terminate(Reason, StateName, State).
+format_status(Type, Data) ->
+ ssl_connection:format_status(Type, Data).
+
%%--------------------------------------------------------------------
%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State0, {Direction, From, To}) ->
State = convert_state(State0, Direction, From, To),
- {ok, StateName, State};
+ {?GEN_STATEM_CB_MODE, StateName, State};
code_change(_OldVsn, StateName, State, _) ->
- {ok, StateName, State}.
-
-format_status(Type, Data) ->
- ssl_connection:format_status(Type, Data).
+ {?GEN_STATEM_CB_MODE, StateName, State}.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -396,7 +526,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us
renegotiation = {false, first},
allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,
start_or_recv_from = undefined,
- send_queue = queue:new(),
protocol_cb = ?MODULE,
tracker = Tracker
}.
@@ -418,80 +547,6 @@ update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
ssl:handle_options(SSLOption, OrigSSLOptions)
end.
-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(Current, Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, #state{negotiated_version = Version} = State) ->
- case decode_alerts(EncAlerts) of
- Alerts = [_|_] ->
- handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
- #alert{} = Alert ->
- handle_own_alert(Alert, Version, Current, State)
- end;
-next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
- State0 = #state{protocol_buffers =
- #protocol_buffers{tls_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, HState0}) ->
- HState = handle_sni_extension(Packet, HState0),
- Version = Packet#client_hello.client_version,
- Hs0 = ssl_handshake:init_handshake_history(),
- Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
- ?MODULE:SName(Packet, HState#state{tls_handshake_history=Hs1,
- renegotiation = {true, peer}});
- ({Packet, Raw}, {next_state, SName, HState0 = #state{tls_handshake_history=Hs0}}) ->
- HState = handle_sni_extension(Packet, HState0),
- Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
- ?MODULE:SName(Packet, HState#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{tls_packets = Packets,
- tls_handshake_buffer = Buf}},
- handle_tls_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) ->
- case read_application_data(Data, State0) of
- Stop = {stop,_,_} ->
- Stop;
- {Record, State} ->
- next_state(StateName, StateName, Record, State)
- end;
-next_state(Current, Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} =
- _ChangeCipher,
- #state{connection_states = ConnectionStates0} = State0)
- when Next == cipher; Next == abbreviated ->
- ConnectionStates1 =
- ssl_record:activate_pending_connection_state(ConnectionStates0, read),
- {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}),
- next_state(Current, Next, Record, State#state{expecting_finished = true});
-next_state(Current, _Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} =
- _ChangeCipher, #state{negotiated_version = Version} = State) ->
- handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, Current, State);
-next_state(Current, Next, #ssl_tls{type = _Unknown}, State0) ->
- %% Ignore unknown type
- {Record, State} = next_record(State0),
- next_state(Current, Next, Record, State).
-
next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buffer = Buf0,
tls_cipher_texts = CT0} = Buffers} = State0) ->
case tls_record:get_tls_records(Data, Buf0) of
@@ -504,11 +559,6 @@ next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buf
Alert
end.
-next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = []},
- socket = Socket,
- transport_cb = Transport} = State) ->
- ssl_socket:setopts(Transport, Socket, [{active,once}]),
- {no_record, State};
next_record(#state{protocol_buffers =
#protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]}
= Buffers,
@@ -522,6 +572,11 @@ next_record(#state{protocol_buffers =
#alert{} = Alert ->
{Alert, State}
end;
+next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = []},
+ socket = Socket,
+ transport_cb = Transport} = State) ->
+ ssl_socket:setopts(Transport, Socket, [{active,once}]),
+ {no_record, State};
next_record(State) ->
{no_record, State}.
@@ -533,55 +588,36 @@ next_record_if_active(State =
next_record_if_active(State) ->
next_record(State).
-next_state_connection(StateName, #state{send_queue = Queue0,
- negotiated_version = Version,
- socket = Socket,
- transport_cb = Transport,
- connection_states = ConnectionStates0
- } = State) ->
- %% Send queued up data that was queued while renegotiating
- case queue:out(Queue0) of
- {{value, {From, Data}}, Queue} ->
- {Msgs, ConnectionStates} =
- ssl_record:encode_data(Data, Version, ConnectionStates0),
- Result = Transport:send(Socket, Msgs),
- gen_fsm:reply(From, Result),
- next_state_connection(StateName,
- State#state{connection_states = ConnectionStates,
- send_queue = Queue});
- {empty, Queue0} ->
- next_state_is_connection(StateName, State)
- end.
-
-%% In next_state_is_connection/1: clear tls_handshake,
-%% premaster_secret and public_key_info (only needed during handshake)
-%% to reduce memory foot print of a connection.
-next_state_is_connection(_, State =
- #state{start_or_recv_from = RecvFrom,
- socket_options =
- #socket_options{active = false}}) when RecvFrom =/= undefined ->
- passive_receive(State#state{premaster_secret = undefined,
- public_key_info = undefined,
- tls_handshake_history = ssl_handshake:init_handshake_history()}, connection);
-
-next_state_is_connection(StateName, State0) ->
- {Record, State} = next_record_if_active(State0),
- next_state(StateName, connection, Record, State#state{premaster_secret = undefined,
- public_key_info = undefined,
- tls_handshake_history = ssl_handshake:init_handshake_history()}).
-
passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
case Buffer of
<<>> ->
{Record, State} = next_record(State0),
- next_state(StateName, StateName, Record, State);
+ next_event(StateName, Record, State);
_ ->
- case read_application_data(<<>>, State0) of
- Stop = {stop, _, _} ->
- Stop;
- {Record, State} ->
- next_state(StateName, StateName, Record, State)
- end
+ {Record, State} = read_application_data(<<>>, State0),
+ next_event(StateName, Record, State)
+ end.
+
+next_event(StateName, Record, State) ->
+ next_event(StateName, Record, State, []).
+
+next_event(connection = StateName, no_record, State0, Actions) ->
+ case next_record_if_active(State0) of
+ {no_record, State} ->
+ ssl_connection:hibernate_after(StateName, State, Actions);
+ {#ssl_tls{} = Record, State} ->
+ {next_state, StateName, State, [{next_event, internal, {tls_record, Record}} | Actions]};
+ {#alert{} = Alert, State} ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end;
+next_event(StateName, Record, State, Actions) ->
+ case Record of
+ no_record ->
+ {next_state, StateName, State, Actions};
+ #ssl_tls{} = Record ->
+ {next_state, StateName, State, [{next_event, internal, {tls_record, Record}} | Actions]};
+ #alert{} = Alert ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
read_application_data(Data, #state{user_application = {_Mon, Pid},
@@ -625,11 +661,6 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
{stop, normal, State0}
end.
-get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) ->
- infinity;
-get_timeout(#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}}) ->
- HibernateAfter.
-
%% Picks ClientData
get_data(_, _, <<>>) ->
{more, <<>>};
@@ -736,7 +767,7 @@ header(N, Binary) ->
[ByteN | header(N-1, NewBinary)].
send_or_reply(false, _Pid, From, Data) when From =/= undefined ->
- gen_fsm:reply(From, Data);
+ gen_statem:reply(From, Data);
%% Can happen when handling own alert or tcp error/close and there is
%% no outstanding gen_fsm sync events
send_or_reply(false, no_pid, _, _) ->
@@ -747,51 +778,43 @@ send_or_reply(_, Pid, _From, Data) ->
send_user(Pid, Msg) ->
Pid ! Msg.
-handle_tls_handshake(Handle, StateName,
- #state{protocol_buffers =
- #protocol_buffers{tls_packets = [Packet]} = Buffers} = State) ->
- FsmReturn = {next_state, StateName, State#state{protocol_buffers =
- Buffers#protocol_buffers{tls_packets = []}}},
- Handle(Packet, FsmReturn);
-
-handle_tls_handshake(Handle, StateName,
- #state{protocol_buffers =
- #protocol_buffers{tls_packets = [Packet | Packets]} = Buffers} =
- State0) ->
- FsmReturn = {next_state, StateName, State0#state{protocol_buffers =
- Buffers#protocol_buffers{tls_packets =
- Packets}}},
- case Handle(Packet, FsmReturn) of
- {next_state, NextStateName, State, _Timeout} ->
- handle_tls_handshake(Handle, NextStateName, State);
- {next_state, NextStateName, State} ->
- handle_tls_handshake(Handle, NextStateName, State);
- {stop, _,_} = Stop ->
- Stop
- end;
-
-handle_tls_handshake(_Handle, _StateName, #state{}) ->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+tls_handshake_events(Handle, StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{tls_packets = [Packet]} = Buffers} = State0, Acc) ->
+ {State, Event} = Handle(Packet, {StateName,
+ State0#state{protocol_buffers =
+ Buffers#protocol_buffers{tls_packets = []}}}),
+ {State, lists:reverse([Event |Acc])};
+tls_handshake_events(Handle, StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{tls_packets =
+ [Packet | Packets]} = Buffers} = State0, Acc) ->
+ {State, Event} = Handle(Packet, {StateName, State0#state{protocol_buffers =
+ Buffers#protocol_buffers{tls_packets =
+ Packets}}}),
+ tls_handshake_events(Handle, StateName, State, [Event | Acc]);
+
+tls_handshake_events(_Handle, _, #state{}, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
write_application_data(Data0, From,
#state{socket = Socket,
negotiated_version = Version,
transport_cb = Transport,
connection_states = ConnectionStates0,
- send_queue = SendQueue,
socket_options = SockOpts,
ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) ->
Data = encode_packet(Data0, SockOpts),
case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
true ->
- renegotiate(State#state{send_queue = queue:in_r({From, Data}, SendQueue),
- renegotiation = {true, internal}});
+ renegotiate(State#state{renegotiation = {true, internal}},
+ [{next_event, {call, From}, {application_data, Data0}}]);
false ->
{Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0),
Result = Transport:send(Socket, Msgs),
- {reply, Result,
- connection, State#state{connection_states = ConnectionStates}, get_timeout(State)}
+ ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},
+ [{reply, From, Result}])
end.
encode_packet(Data, #socket_options{packet=Packet}) ->
@@ -823,69 +846,73 @@ is_time_to_renegotiate(N, M) when N < M->
false;
is_time_to_renegotiate(_,_) ->
true.
-renegotiate(#state{role = client} = State) ->
+renegotiate(#state{role = client} = State, Actions) ->
%% Handle same way as if server requested
%% the renegotiation
Hs0 = ssl_handshake:init_handshake_history(),
- connection(#hello_request{}, State#state{tls_handshake_history = Hs0});
+ {next_state, connection, State#state{tls_handshake_history = Hs0},
+ [{next_event, internal, #hello_request{}} | Actions]};
+
renegotiate(#state{role = server,
socket = Socket,
transport_cb = Transport,
negotiated_version = Version,
- connection_states = ConnectionStates0} = State0) ->
+ connection_states = ConnectionStates0} = State0, Actions) ->
HelloRequest = ssl_handshake:hello_request(),
Frag = tls_handshake:encode_handshake(HelloRequest, Version),
Hs0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
Transport:send(Socket, BinMsg),
- {Record, State} = next_record(State0#state{connection_states =
- ConnectionStates,
- tls_handshake_history = Hs0}),
- next_state(connection, hello, Record, State#state{allow_renegotiate = true}).
+ State1 = State0#state{connection_states =
+ ConnectionStates,
+ tls_handshake_history = Hs0},
+ {Record, State} = next_record(State1),
+ next_event(hello, Record, State, Actions).
handle_alerts([], Result) ->
Result;
-handle_alerts(_, {stop, _, _} = Stop) ->
- %% If it is a fatal alert immediately close
+handle_alerts(_, {stop,_} = Stop) ->
Stop;
-handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
- handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
-
+handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
+ handle_alerts(Alerts, handle_alert(Alert, StateName, State));
+handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
+ handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
#state{socket = Socket, transport_cb = Transport,
ssl_options = SslOpts, start_or_recv_from = From, host = Host,
port = Port, session = Session, user_application = {_Mon, Pid},
- role = Role, socket_options = Opts, tracker = Tracker} = State) ->
+ role = Role, socket_options = Opts, tracker = Tracker}) ->
invalidate_session(Role, Host, Port, Session),
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role),
- {stop, normal, State};
+ {stop, normal};
handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
StateName, State) ->
handle_normal_shutdown(Alert, StateName, State),
- {stop, {shutdown, peer_close}, State};
+ {stop, {shutdown, peer_close}};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) ->
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
handle_normal_shutdown(Alert, StateName, State),
- {stop, {shutdown, peer_close}, State};
+ {stop, {shutdown, peer_close}};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{ssl_options = SslOpts, renegotiation = {true, From}} = State0) ->
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- gen_fsm:reply(From, {error, renegotiation_rejected}),
+ gen_statem:reply(From, {error, renegotiation_rejected}),
{Record, State} = next_record(State0),
- next_state(StateName, connection, Record, State);
+ %% Go back to connection!
+ next_event(connection, Record, State);
%% Gracefully log and ignore all other warning alerts
handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{ssl_options = SslOpts} = State0) ->
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
{Record, State} = next_record(State0),
- next_state(StateName, StateName, Record, State).
+ next_event(StateName, Record, State).
alert_user(Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role) ->
alert_user(Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role);
@@ -937,7 +964,7 @@ handle_own_alert(Alert, Version, StateName,
catch _:_ ->
ok
end,
- {stop, {shutdown, own_alert}, State}.
+ {stop, {shutdown, own_alert}}.
handle_normal_shutdown(Alert, _, #state{socket = Socket,
transport_cb = Transport,
@@ -954,11 +981,6 @@ handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
start_or_recv_from = RecvFrom, role = Role}) ->
alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
-handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
- Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, {Info, Msg}, State).
-
-
handle_close_alert(Data, StateName, State0) ->
case next_tls_record(Data, State0) of
{#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} ->
@@ -979,27 +1001,6 @@ invalidate_session(client, Host, Port, Session) ->
invalidate_session(server, _, Port, Session) ->
ssl_manager:invalidate_session(Port, Session).
-%% User downgrades connection
-%% When downgrading an TLS connection to a transport connection
-%% we must recive the close message before releasing the
-%% transport socket.
-close({close, {Pid, Timeout}}, Socket, Transport, ConnectionStates, Check) when is_pid(Pid) ->
- ssl_socket:setopts(Transport, Socket, [{active, false}, {packet, ssl_tls}]),
- case Transport:recv(Socket, 0, Timeout) of
- {ok, {ssl_tls, Socket, ?ALERT, Version, Fragment}} ->
- case tls_record:decode_cipher_text(#ssl_tls{type = ?ALERT,
- version = Version,
- fragment = Fragment
- }, ConnectionStates, Check) of
- {#ssl_tls{fragment = Plain}, _} ->
- [Alert| _] = decode_alerts(Plain),
- downgrade(Alert, Transport, Socket, Pid)
- end;
- {error, timeout} ->
- {error, timeout};
- _ ->
- {error, no_tls_close}
- end;
%% User closes or recursive call!
close({close, Timeout}, Socket, Transport = gen_tcp, _,_) ->
ssl_socket:setopts(Transport, Socket, [{active, false}]),
@@ -1020,15 +1021,11 @@ close({shutdown, own_alert}, Socket, Transport = gen_tcp, ConnectionStates, Chec
%% with the network but we want to maximise the odds that
%% peer application gets all data sent on the tcp connection.
close({close, ?DEFAULT_TIMEOUT}, Socket, Transport, ConnectionStates, Check);
+close(downgrade, _,_,_,_) ->
+ ok;
%% Other
close(_, Socket, Transport, _,_) ->
Transport:close(Socket).
-downgrade(#alert{description = ?CLOSE_NOTIFY}, Transport, Socket, Pid) ->
- ssl_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]),
- Transport:controlling_process(Socket, Pid),
- {ok, Socket};
-downgrade(_, _,_,_) ->
- {error, no_tls_close}.
convert_state(#state{ssl_options = Options} = State, up, "5.3.5", "5.3.6") ->
State#state{ssl_options = convert_options_partial_chain(Options, up)};
@@ -1069,4 +1066,3 @@ handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) ->
end;
handle_sni_extension(_, State0) ->
State0.
-
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index ef718c13df..102dbba198 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -278,10 +278,11 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
{Version, SessionId, ConnectionStates, ProtoExt, Protocol}
end.
-available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) ->
+available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when
+ (Major >= 3) andalso (Minor >= 3) ->
SupportedHashSigns;
available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns,
- _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) ->
+ _, {Major, Minor}) when (Major >= 3) andalso (Minor >= 3) ->
ordsets:intersection(ClientHashSigns, SupportedHashSigns);
available_signature_algs(_, _, _, _) ->
undefined.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 50313e6a22..9341d2cae7 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -88,7 +88,8 @@ basic_tests() ->
connect_dist,
clear_pem_cache,
defaults,
- fallback
+ fallback,
+ cipher_format
].
options_tests() ->
@@ -144,7 +145,8 @@ api_tests() ->
versions_option,
server_name_indication_option,
accept_pool,
- new_options_in_accept
+ new_options_in_accept,
+ prf
].
session_tests() ->
@@ -168,6 +170,7 @@ renegotiate_tests() ->
cipher_tests() ->
[cipher_suites,
+ cipher_suites_mix,
ciphers_rsa_signed_certs,
ciphers_rsa_signed_certs_openssl_names,
ciphers_dsa_signed_certs,
@@ -324,6 +327,31 @@ init_per_testcase(rizzo, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 40}),
Config;
+init_per_testcase(prf, Config) ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 40}),
+ case ?config(tc_group_path, Config) of
+ [] -> Prop = [];
+ [Prop] -> Prop
+ end,
+ case ?config(name, Prop) of
+ undefined -> TlsVersions = [sslv3, tlsv1, 'tlsv1.1', 'tlsv1.2'];
+ TlsVersion when is_atom(TlsVersion) ->
+ TlsVersions = [TlsVersion]
+ end,
+ PRFS=[md5, sha, sha256, sha384, sha512],
+ %All are the result of running tls_v1:prf(PrfAlgo, <<>>, <<>>, <<>>, 16)
+ %with the specified PRF algorithm
+ ExpectedPrfResults=
+ [{md5, <<96,139,180,171,236,210,13,10,28,32,2,23,88,224,235,199>>},
+ {sha, <<95,3,183,114,33,169,197,187,231,243,19,242,220,228,70,151>>},
+ {sha256, <<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,155>>},
+ {sha384, <<153,182,217,96,186,130,105,85,65,103,123,247,146,91,47,106>>},
+ {sha512, <<145,8,98,38,243,96,42,94,163,33,53,49,241,4,127,28>>},
+ %TLS 1.0 and 1.1 PRF:
+ {md5sha, <<63,136,3,217,205,123,200,177,251,211,17,229,132,4,173,80>>}],
+ TestPlan = prf_create_plan(TlsVersions, PRFS, ExpectedPrfResults),
+ [{prf_test_plan, TestPlan} | Config];
init_per_testcase(TestCase, Config) when TestCase == ssl_accept_timeout;
TestCase == client_closes_socket;
@@ -429,6 +457,25 @@ new_options_in_accept(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+prf() ->
+ [{doc,"Test that ssl:prf/5 uses the negotiated PRF."}].
+prf(Config) when is_list(Config) ->
+ TestPlan = ?config(prf_test_plan, Config),
+ case TestPlan of
+ [] -> ct:fail({error, empty_prf_test_plan});
+ _ -> lists:foreach(fun(Suite) ->
+ lists:foreach(
+ fun(Test) ->
+ V = ?config(tls_ver, Test),
+ C = ?config(ciphers, Test),
+ E = ?config(expected, Test),
+ P = ?config(prf, Test),
+ prf_run_test(Config, V, C, E, P)
+ end, Suite)
+ end, TestPlan)
+ end.
+
+%%--------------------------------------------------------------------
connection_info() ->
[{doc,"Test the API function ssl:connection_information/1"}].
@@ -718,21 +765,27 @@ clear_pem_cache(Config) when is_list(Config) ->
State = ssl_test_lib:state(Prop),
[_,FilRefDb |_] = element(6, State),
{Server, Client} = basic_verify_test_no_close(Config),
- 2 = ets:info(FilRefDb, size),
+ CountReferencedFiles = fun({_,-1}, Acc) ->
+ Acc;
+ ({_, N}, Acc) ->
+ N + Acc
+ end,
+
+ 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
ssl:clear_pem_cache(),
_ = sys:get_status(whereis(ssl_manager)),
{Server1, Client1} = basic_verify_test_no_close(Config),
- 4 = ets:info(FilRefDb, size),
+ 4 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
- ct:sleep(5000),
+ ct:sleep(2000),
_ = sys:get_status(whereis(ssl_manager)),
- 2 = ets:info(FilRefDb, size),
+ 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
ssl_test_lib:close(Server1),
ssl_test_lib:close(Client1),
- ct:sleep(5000),
+ ct:sleep(2000),
_ = sys:get_status(whereis(ssl_manager)),
- 0 = ets:info(FilRefDb, size).
+ 0 = ets:foldl(CountReferencedFiles, 0, FilRefDb).
%%--------------------------------------------------------------------
@@ -763,6 +816,14 @@ fallback(Config) when is_list(Config) ->
Client, {error,{tls_alert,"inappropriate fallback"}}).
%%--------------------------------------------------------------------
+cipher_format() ->
+ [{doc, "Test that cipher conversion from tuples to binarys works"}].
+cipher_format(Config) when is_list(Config) ->
+ {ok, Socket} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
+ ssl:close(Socket).
+
+%%--------------------------------------------------------------------
+
peername() ->
[{doc,"Test API function peername/1"}].
@@ -913,6 +974,31 @@ cipher_suites(Config) when is_list(Config) ->
[_|_] =ssl:cipher_suites(openssl).
%%--------------------------------------------------------------------
+cipher_suites_mix() ->
+ [{doc,"Test to have old and new cipher suites at the same time"}].
+
+cipher_suites_mix(Config) when is_list(Config) ->
+ CipherSuites = [{ecdh_rsa,aes_128_cbc,sha256,sha256}, {rsa,aes_128_cbc,sha}],
+ 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_active, []}},
+ {options, 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_active, []}},
+ {options, [{ciphers, CipherSuites} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
socket_options() ->
[{doc,"Test API function getopts/2 and setopts/2"}].
@@ -1555,7 +1641,7 @@ tcp_connect_big(Config) when is_list(Config) ->
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
TcpOpts = [binary, {reuseaddr, true}],
- Rand = crypto:rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1),
+ Rand = crypto:strong_rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1),
Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
{from, self()},
{timeout, 5000},
@@ -2530,6 +2616,13 @@ der_input(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
DHParamFile = filename:join(DataDir, "dHParam.pem"),
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ [CADb | _] = element(6, State),
+
+ Size = ets:info(CADb, size),
+
SeverVerifyOpts = ?config(server_verification_opts, Config),
{ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
SeverVerifyOpts]),
@@ -2557,13 +2650,8 @@ 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),
+ Size = ets:info(CADb, size).
- {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
- [_, _,_, _, Prop] = StatusInfo,
- State = ssl_test_lib:state(Prop),
- [CADb | _] = element(6, State),
- [] = ets:tab2list(CADb).
-
%%--------------------------------------------------------------------
der_input_opts(Opts) ->
Certfile = proplists:get_value(certfile, Opts),
@@ -3025,6 +3113,7 @@ hibernate(Config) ->
{current_function, _} =
process_info(Pid, current_function),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
timer:sleep(1100),
{current_function, {erlang, hibernate, 3}} =
@@ -3058,15 +3147,29 @@ hibernate_right_away(Config) ->
Server1 = ssl_test_lib:start_server(StartServerOpts),
Port1 = ssl_test_lib:inet_port(Server1),
- {Client1, #sslsocket{}} = ssl_test_lib:start_client(StartClientOpts ++
+ {Client1, #sslsocket{pid = Pid1}} = ssl_test_lib:start_client(StartClientOpts ++
[{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server1, ok, Client1, ok),
+
+ {current_function, {erlang, hibernate, 3}} =
+ process_info(Pid1, current_function),
+
ssl_test_lib:close(Server1),
ssl_test_lib:close(Client1),
Server2 = ssl_test_lib:start_server(StartServerOpts),
Port2 = ssl_test_lib:inet_port(Server2),
- {Client2, #sslsocket{}} = ssl_test_lib:start_client(StartClientOpts ++
+ {Client2, #sslsocket{pid = Pid2}} = ssl_test_lib:start_client(StartClientOpts ++
[{port, Port2}, {options, [{hibernate_after, 1}|ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server2, ok, Client2, ok),
+
+ ct:sleep(100), %% Schedule out
+
+ {current_function, {erlang, hibernate, 3}} =
+ process_info(Pid2, current_function),
+
ssl_test_lib:close(Server2),
ssl_test_lib:close(Client2).
@@ -3651,6 +3754,81 @@ basic_test(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+prf_create_plan(TlsVersions, PRFs, Results) ->
+ lists:foldl(fun(Ver, Acc) ->
+ A = prf_ciphers_and_expected(Ver, PRFs, Results),
+ [A|Acc]
+ end, [], TlsVersions).
+prf_ciphers_and_expected(TlsVer, PRFs, Results) ->
+ case TlsVer of
+ TlsVer when TlsVer == sslv3 orelse TlsVer == tlsv1
+ orelse TlsVer == 'tlsv1.1' ->
+ Ciphers = ssl:cipher_suites(),
+ {_, Expected} = lists:keyfind(md5sha, 1, Results),
+ [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, {prf, md5sha}]];
+ 'tlsv1.2' ->
+ lists:foldl(
+ fun(PRF, Acc) ->
+ Ciphers = prf_get_ciphers(TlsVer, PRF),
+ case Ciphers of
+ [] ->
+ ct:log("No ciphers for PRF algorithm ~p. Skipping.", [PRF]),
+ Acc;
+ Ciphers ->
+ {_, Expected} = lists:keyfind(PRF, 1, Results),
+ [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected},
+ {prf, PRF}] | Acc]
+ end
+ end, [], PRFs)
+ end.
+prf_get_ciphers(TlsVer, PRF) ->
+ case TlsVer of
+ 'tlsv1.2' ->
+ lists:filter(
+ fun(C) when tuple_size(C) == 4 andalso
+ element(4, C) == PRF ->
+ true;
+ (_) -> false
+ end, ssl:cipher_suites())
+ end.
+prf_run_test(_, TlsVer, [], _, Prf) ->
+ ct:fail({error, cipher_list_empty, TlsVer, Prf});
+prf_run_test(Config, TlsVer, Ciphers, Expected, Prf) ->
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ BaseOpts = [{active, true}, {versions, [TlsVer]}, {ciphers, Ciphers}],
+ ServerOpts = BaseOpts ++ ?config(server_opts, Config),
+ ClientOpts = BaseOpts ++ ?config(client_opts, Config),
+ Server = ssl_test_lib:start_server(
+ [{node, ServerNode}, {port, 0}, {from, self()},
+ {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname}, {from, self()},
+ {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
+ {options, ClientOpts}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+prf_verify_value(Socket, TlsVer, Expected, Algo) ->
+ Ret = ssl:prf(Socket, <<>>, <<>>, [<<>>], 16),
+ case TlsVer of
+ sslv3 ->
+ case Ret of
+ {error, undefined} -> ok;
+ _ ->
+ {error, {expected, {error, undefined},
+ got, Ret, tls_ver, TlsVer, prf_algorithm, Algo}}
+ end;
+ _ ->
+ case Ret of
+ {ok, Expected} -> ok;
+ {ok, Val} -> {error, {expected, Expected, got, Val, tls_ver, TlsVer,
+ prf_algorithm, Algo}}
+ end
+ end.
+
send_recv_result_timeout_client(Socket) ->
{error, timeout} = ssl:recv(Socket, 11, 500),
ssl:send(Socket, "Hello world"),
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 66ccbb1da4..49c0b9c5a1 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -52,8 +52,8 @@ groups() ->
{error_handling, [],error_handling_tests()}].
tests() ->
- [server_verify_peer,
- server_verify_none,
+ [verify_peer,
+ verify_none,
server_require_peer_cert_ok,
server_require_peer_cert_fail,
server_require_peer_cert_partial_chain,
@@ -110,6 +110,17 @@ init_per_group(_, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(TestCase, Config) when TestCase == cert_expired;
+ TestCase == invalid_signature_client;
+ TestCase == invalid_signature_server;
+ TestCase == extended_key_usage_verify_none;
+ TestCase == extended_key_usage_verify_peer;
+ TestCase == critical_extension_verify_none;
+ TestCase == critical_extension_verify_peer;
+ TestCase == no_authority_key_identifier;
+ TestCase == no_authority_key_identifier_and_nonstandard_encoding->
+ ssl:clear_pem_cache(),
+ init_per_testcase(common, Config);
init_per_testcase(_TestCase, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 5}),
@@ -122,9 +133,9 @@ end_per_testcase(_TestCase, Config) ->
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-server_verify_peer() ->
- [{doc,"Test server option verify_peer"}].
-server_verify_peer(Config) when is_list(Config) ->
+verify_peer() ->
+ [{doc,"Test option verify_peer"}].
+verify_peer(Config) when is_list(Config) ->
ClientOpts = ?config(client_verification_opts, Config),
ServerOpts = ?config(server_verification_opts, Config),
Active = ?config(active, Config),
@@ -147,10 +158,10 @@ server_verify_peer(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-server_verify_none() ->
- [{doc,"Test server option verify_none"}].
+verify_none() ->
+ [{doc,"Test option verify_none"}].
-server_verify_none(Config) when is_list(Config) ->
+verify_none(Config) when is_list(Config) ->
ClientOpts = ?config(client_verification_opts, Config),
ServerOpts = ?config(server_verification_opts, Config),
Active = ?config(active, Config),
@@ -220,18 +231,21 @@ server_require_peer_cert_ok(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ?config(server_verification_opts, Config)],
ClientOpts = ?config(client_verification_opts, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, 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} | ServerOpts]}]),
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active} | 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} | ClientOpts]}]),
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
@@ -313,6 +327,8 @@ server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) ->
| ?config(server_verification_opts, Config)],
ClientOpts = ?config(client_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, Config),
{ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
[{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs),
@@ -328,16 +344,17 @@ server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{cacerts, [IntermidiateCA]},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active},
+ {cacerts, [IntermidiateCA]},
{partial_chain, PartialChain} |
proplists:delete(cacertfile, 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_active, []}},
- {options, ClientOpts}]),
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
@@ -522,32 +539,6 @@ verify_fun_always_run_server(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-client_verify_none_passive() ->
- [{doc,"Test client option verify_none"}].
-
-client_verify_none_passive(Config) when is_list(Config) ->
- 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}
- | 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},
- {verify, verify_none}
- | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-%%--------------------------------------------------------------------
cert_expired() ->
[{doc,"Test server with expired certificate"}].
@@ -616,64 +607,6 @@ two_digits_str(N) ->
lists:flatten(io_lib:format("~p", [N])).
%%--------------------------------------------------------------------
-
-client_verify_none_active() ->
- [{doc,"Test client option verify_none"}].
-
-client_verify_none_active(Config) when is_list(Config) ->
- 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_active, []}},
- {options, [{active, true}
- | 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_active, []}},
- {options, [{active, true},
- {verify, verify_none}
- | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-client_verify_none_active_once() ->
- [{doc,"Test client option verify_none"}].
-
-client_verify_none_active_once(Config) when is_list(Config) ->
- 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_active, []}},
- {options, [{active, once} | 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_active_once,
- []}},
- {options, [{active, once},
- {verify, verify_none}
- | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
extended_key_usage_verify_peer() ->
[{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode"}].
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index bd0ddde090..e7cbfa63f4 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -194,7 +194,7 @@ payload(Config) when is_list(Config) ->
ok = apply_on_ssl_node(
NH2,
fun () ->
- Msg = crypto:rand_bytes(100000),
+ Msg = crypto:strong_rand_bytes(100000),
SslPid ! {self(), Msg},
receive
{SslPid, Msg} ->
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 2cd23eb3b8..38cc3532d8 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -349,7 +349,7 @@ wait_for_result(Pid, Msg) ->
user_lookup(psk, _Identity, UserState) ->
{ok, UserState};
user_lookup(srp, Username, _UserState) ->
- Salt = ssl:random_bytes(16),
+ Salt = ssl_cipher:random_bytes(16),
UserPassHash = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, <<"secret">>])]),
{ok, {srp_1024, Salt, UserPassHash}}.
@@ -905,8 +905,8 @@ anonymous_suites() ->
{dh_anon, '3des_ede_cbc', sha},
{dh_anon, aes_128_cbc, sha},
{dh_anon, aes_256_cbc, sha},
- {dh_anon, aes_128_gcm, null},
- {dh_anon, aes_256_gcm, null},
+ {dh_anon, aes_128_gcm, null, sha256},
+ {dh_anon, aes_256_gcm, null, sha384},
{ecdh_anon,rc4_128,sha},
{ecdh_anon,'3des_ede_cbc',sha},
{ecdh_anon,aes_128_cbc,sha},
@@ -933,12 +933,12 @@ psk_suites() ->
{rsa_psk, aes_256_cbc, sha},
{rsa_psk, aes_128_cbc, sha256},
{rsa_psk, aes_256_cbc, sha384},
- {psk, aes_128_gcm, null},
- {psk, aes_256_gcm, null},
- {dhe_psk, aes_128_gcm, null},
- {dhe_psk, aes_256_gcm, null},
- {rsa_psk, aes_128_gcm, null},
- {rsa_psk, aes_256_gcm, null}],
+ {psk, aes_128_gcm, null, sha256},
+ {psk, aes_256_gcm, null, sha384},
+ {dhe_psk, aes_128_gcm, null, sha256},
+ {dhe_psk, aes_256_gcm, null, sha384},
+ {rsa_psk, aes_128_gcm, null, sha256},
+ {rsa_psk, aes_256_gcm, null, sha384}],
ssl_cipher:filter_suites(Suites).
psk_anon_suites() ->
@@ -1041,10 +1041,13 @@ receive_rizzo_duong_beast() ->
end
end.
-state([{data,[{"State", State}]} | _]) ->
- State;
-state([{data,[{"StateData", State}]} | _]) ->
+
+state([{data,[{"State", {_StateName, StateData}}]} | _]) -> %% gen_statem
+ StateData;
+state([{data,[{"State", State}]} | _]) -> %% gen_server
State;
+state([{data,[{"StateData", State}]} | _]) -> %% gen_fsm
+ State;
state([_ | Rest]) ->
state(Rest).
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 47cb2909fb..3b51fa8c6b 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 7.3
+SSL_VSN = 8.0
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 196c86748f..26602764a6 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2015. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -68,6 +68,7 @@ XML_REF3_FILES = \
gen_event.xml \
gen_fsm.xml \
gen_server.xml \
+ gen_statem.xml \
io.xml \
io_lib.xml \
lib.xml \
diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml
index 5402fd0163..933157fc34 100644
--- a/lib/stdlib/doc/src/binary.xml
+++ b/lib/stdlib/doc/src/binary.xml
@@ -366,7 +366,7 @@
<code>
1> binary:matches(&lt;&lt;"abcde"&gt;&gt;,
- [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"bc"&gt;&gt;>,&lt;&lt;"de"&gt;&gt;],[]).
+ [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"bc"&gt;&gt;,&lt;&lt;"de"&gt;&gt;],[]).
[{1,4}]
</code>
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 37e7c6ed17..835e252704 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -31,12 +31,23 @@
<module>gen_fsm</module>
<modulesummary>Generic Finite State Machine Behaviour</modulesummary>
<description>
+ <note>
+ <p>
+ There is a new behaviour
+ <seealso marker="gen_statem"><c>gen_statem</c></seealso>
+ that is intended to replace <c>gen_fsm</c> for new code.
+ It has the same features and add some really useful.
+ This module will not be removed for the foreseeable future
+ to keep old state machine implementations running.
+ </p>
+ </note>
<p>A behaviour module for implementing a finite state machine.
A generic finite state machine process (gen_fsm) implemented
using this module will have a standard set of interface functions
and include functionality for tracing and error reporting. It will
also fit into an OTP supervision tree. Refer to
- <seealso marker="doc/design_principles:fsm">OTP Design Principles</seealso> for more information.</p>
+ <seealso marker="doc/design_principles:fsm">OTP Design Principles</seealso> for more information.
+ </p>
<p>A gen_fsm assumes all specific parts to be located in a callback
module exporting a pre-defined set of functions. The relationship
between the behaviour functions and the callback functions can be
@@ -848,6 +859,7 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<title>SEE ALSO</title>
<p><seealso marker="gen_event">gen_event(3)</seealso>,
<seealso marker="gen_server">gen_server(3)</seealso>,
+ <seealso marker="gen_statem">gen_statem(3)</seealso>,
<seealso marker="supervisor">supervisor(3)</seealso>,
<seealso marker="proc_lib">proc_lib(3)</seealso>,
<seealso marker="sys">sys(3)</seealso></p>
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index 02da11250a..10dc978afc 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -715,6 +715,7 @@ gen_server:abcast -----> Module:handle_cast/2
<title>SEE ALSO</title>
<p><seealso marker="gen_event">gen_event(3)</seealso>,
<seealso marker="gen_fsm">gen_fsm(3)</seealso>,
+ <seealso marker="gen_statem">gen_statem(3)</seealso>,
<seealso marker="supervisor">supervisor(3)</seealso>,
<seealso marker="proc_lib">proc_lib(3)</seealso>,
<seealso marker="sys">sys(3)</seealso></p>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
new file mode 100644
index 0000000000..b1d9799917
--- /dev/null
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -0,0 +1,1671 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2016</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>gen_statem</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>gen_statem</module>
+ <modulesummary>Generic state machine behavior.</modulesummary>
+ <description>
+ <p>
+ This behavior module provides a state machine. Two
+ <seealso marker="#type-callback_mode"><em>callback modes</em></seealso>
+ are supported:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>One for finite-state machines
+ (<seealso marker="gen_fsm"><c>gen_fsm</c></seealso> like),
+ which requires the state to be an atom and uses that state as
+ the name of the current callback function
+ </p>
+ </item>
+ <item>
+ <p>One without restriction on the state data type
+ that uses one callback function for all states
+ </p>
+ </item>
+ </list>
+ <note>
+ <p>
+ This is a new behavior in Erlang/OTP 19.0.
+ It has been thoroughly reviewed, is stable enough
+ to be used by at least two heavy OTP applications, and is here to stay.
+ Depending on user feedback, we do not expect
+ but can find it necessary to make minor
+ not backward compatible changes into Erlang/OTP 20.0.
+ </p>
+ </note>
+ <p>
+ The <c>gen_statem</c> behavior is intended to replace
+ <seealso marker="gen_fsm"><c>gen_fsm</c></seealso> for new code.
+ It has the same features and adds some really useful:
+ </p>
+ <list type="bulleted">
+ <item>State code is gathered.</item>
+ <item>The state can be any term.</item>
+ <item>Events can be postponed.</item>
+ <item>Events can be self-generated.</item>
+ <item>A reply can be sent from a later state.</item>
+ <item>There can be multiple <c>sys</c> traceable replies.</item>
+ </list>
+ <p>
+ The callback model(s) for <c>gen_statem</c> differs from
+ the one for <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>,
+ but it is still fairly easy to rewrite
+ from <c>gen_fsm</c> to <c>gen_statem</c>.
+ </p>
+ <p>
+ A generic state machine process (<c>gen_statem</c>) implemented
+ using this module has a standard set of interface functions
+ and includes functionality for tracing and error reporting.
+ It also fits into an OTP supervision tree. For more information, see
+ <seealso marker="doc/design_principles:statem">OTP Design Principles</seealso>.
+ </p>
+ <p>
+ A <c>gen_statem</c> assumes all specific parts to be located in a
+ callback module exporting a predefined set of functions.
+ The relationship between the behavior functions and the callback
+ functions is as follows:</p>
+ <pre>
+gen_statem module Callback module
+----------------- ---------------
+gen_statem:start
+gen_statem:start_link -----> Module:init/1
+
+gen_statem:stop -----> Module:terminate/3
+
+gen_statem:call
+gen_statem:cast
+erlang:send
+erlang:'!' -----> Module:StateName/3
+ Module:handle_event/4
+
+- -----> Module:terminate/3
+
+- -----> Module:code_change/4</pre>
+ <p>
+ Events are of different
+ <seealso marker="#type-event_type">types</seealso>,
+ so the callback functions can know the origin of an event
+ and how to respond.
+ </p>
+ <p>
+ If a callback function fails or returns a bad value,
+ the <c>gen_statem</c> terminates. However, an exception of class
+ <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>
+ is not regarded as an error but as a valid return.
+ </p>
+ <marker id="state_function"/>
+ <p>
+ The "<em>state function</em>" for a specific
+ <seealso marker="#type-state">state</seealso>
+ in a <c>gen_statem</c> is the callback function that is called
+ for all events in this state. It is selected depending on which
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ that the implementation specifies when the server starts.
+ </p>
+ <p>
+ When the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ is <c>state_functions</c>, the state must be an atom and
+ is used as the state function name; see
+ <seealso marker="#Module:StateName/3"><c>Module:StateName/3</c></seealso>.
+ This gathers all code for a specific state
+ in one function and hence dispatches on state first.
+ Notice that in this mode
+ the mandatory callback function
+ <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ makes the state name <c>terminate</c> unusable.
+ </p>
+ <p>
+ When the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ is <c>handle_event_function</c>, the state can be any term
+ and the state function name is
+ <seealso marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>.
+ This makes it easy to dispatch on state or on event as you desire.
+ Be careful about which events you handle in which
+ states so that you do not accidentally postpone an event
+ forever creating an infinite busy loop.
+ </p>
+ <p>
+ The <c>gen_statem</c> enqueues incoming events in order of arrival
+ and presents these to the
+ <seealso marker="#state_function">state function</seealso>
+ in that order. The state function can postpone an event
+ so it is not retried in the current state.
+ After a state change the queue restarts with the postponed events.
+ </p>
+ <p>
+ The <c>gen_statem</c> event queue model is sufficient
+ to emulate the normal process message queue with selective receive.
+ Postponing an event corresponds to not matching it
+ in a receive statement, and changing states corresponds
+ to entering a new receive statement.
+ </p>
+ <p>
+ The <seealso marker="#state_function">state function</seealso>
+ can insert events using the
+ <seealso marker="#type-action"><c>action()</c></seealso>
+ <c>next_event</c>
+ and such an event is inserted as the next to present
+ to the state function. That is, as if it is
+ the oldest incoming event. A dedicated
+ <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <c>internal</c> can be used for such events making them impossible
+ to mistake for external events.
+ </p>
+ <p>
+ Inserting an event replaces the trick of calling your own
+ state handling functions that you often would have to
+ resort to in, for example,
+ <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>
+ to force processing an inserted event before others.
+ </p>
+ <note>
+ <p>If you in <c>gen_statem</c>, for example, postpone
+ an event in one state and then call another state function
+ of yours, you have not changed states and hence the postponed event
+ is not retried, which is logical but can be confusing.
+ </p>
+ </note>
+ <p>
+ For the details of a state transition, see type
+ <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>.
+ </p>
+ <p>
+ A <c>gen_statem</c> handles system messages as described in
+ <seealso marker="sys"><c>sys</c></seealso>.
+ The <c>sys</c> module can be used for debugging a <c>gen_statem</c>.
+ </p>
+ <p>
+ Notice that a <c>gen_statem</c> does not trap exit signals
+ automatically, this must be explicitly initiated in
+ the callback module (by calling
+ <seealso marker="erts:erlang#process_flag/2"><c>process_flag(trap_exit, true)</c></seealso>.
+ </p>
+ <p>
+ Unless otherwise stated, all functions in this module fail if
+ the specified <c>gen_statem</c> does not exist or
+ if bad arguments are specified.
+ </p>
+ <p>
+ The <c>gen_statem</c> process can go into hibernation; see
+ <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>.
+ It is done when a
+ <seealso marker="#state_function">state function</seealso> or
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ specifies <c>hibernate</c> in the returned
+ <seealso marker="#type-action"><c>Actions</c></seealso>
+ list. This feature can be useful to reclaim process heap memory
+ while the server is expected to be idle for a long time.
+ However, use this feature with care,
+ as hibernation can be too costly
+ to use after every event; see
+ <seealso marker="erts:erlang#hibernate/3"><c>erlang:hibernate/3</c></seealso>.
+ </p>
+ </description>
+
+ <section>
+ <title>Example</title>
+ <p>
+ The following example shows a simple pushbutton model
+ for a toggling pushbutton implemented with
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <c>state_functions</c>.
+ You can push the button and it replies if it went on or off,
+ and you can ask for a count of how many times it has been
+ pushed to switch on.
+ </p>
+ <p>The following is the complete callback module file
+ <c>pushbutton.erl</c>:</p>
+ <code type="erl">
+-module(pushbutton).
+-behaviour(gen_statem).
+
+-export([start/0,push/0,get_count/0,stop/0]).
+-export([terminate/3,code_change/4,init/1]).
+-export([on/3,off/3]).
+
+name() -> pushbutton_statem. % The registered server name
+callback_mode() -> state_functions.
+
+%% API. This example uses a registered name name()
+%% and does not link to the caller.
+start() ->
+ gen_statem:start({local,name()}, ?MODULE, [], []).
+push() ->
+ gen_statem:call(name(), push).
+get_count() ->
+ gen_statem:call(name(), get_count).
+stop() ->
+ gen_statem:stop(name()).
+
+%% Mandatory callback functions
+terminate(_Reason, _State, _Data) ->
+ void.
+code_change(_Vsn, State, Data, _Extra) ->
+ {callback_mode(),State,Data}.
+init([]) ->
+ %% Set the callback mode and initial state + data.
+ %% Data is used only as a counter.
+ State = off, Data = 0,
+ {callback_mode(),State,Data}.
+
+
+%%% State functions
+
+off({call,From}, push, Data) ->
+ %% Go to 'on', increment count and reply
+ %% that the resulting status is 'on'
+ {next_state,on,Data+1,[{reply,From,on}]};
+off(EventType, EventContent, Data) ->
+ handle_event(EventType, EventContent, Data).
+
+on({call,From}, push, Data) ->
+ %% Go to 'off' and reply that the resulting status is 'off'
+ {next_state,off,Data,[{reply,From,off}]};
+on(EventType, EventContent, Data) ->
+ handle_event(EventType, EventContent, Data).
+
+%% Handle events common to all states
+handle_event({call,From}, get_count, Data) ->
+ %% Reply with the current count
+ {keep_state,Data,[{reply,From,Data}]};
+handle_event(_, _, Data) ->
+ %% Ignore all other events
+ {keep_state,Data}.
+ </code>
+ <p>The following is a shell session when running it:</p>
+ <pre>
+1> pushbutton:start().
+{ok,&lt;0.36.0>}
+2> pushbutton:get_count().
+0
+3> pushbutton:push().
+on
+4> pushbutton:get_count().
+1
+5> pushbutton:push().
+off
+6> pushbutton:get_count().
+1
+7> pushbutton:stop().
+ok
+8> pushbutton:push().
+** exception exit: {noproc,{gen_statem,call,[pushbutton_statem,push,infinity]}}
+ in function gen:do_for_proc/2 (gen.erl, line 261)
+ in call from gen_statem:call/3 (gen_statem.erl, line 386)
+ </pre>
+ <p>
+ To compare styles, here follows the same example using
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <c>state_functions</c>, or rather the code to replace
+ from function <c>init/1</c> of the <c>pushbutton.erl</c>
+ example file above:
+ </p>
+ <code type="erl">
+init([]) ->
+ %% Set the callback mode and initial state + data.
+ %% Data is used only as a counter.
+ State = off, Data = 0,
+ {handle_event_function,State,Data}.
+
+
+%%% Event handling
+
+handle_event({call,From}, push, off, Data) ->
+ %% Go to 'on', increment count and reply
+ %% that the resulting status is 'on'
+ {next_state,on,Data+1,[{reply,From,on}]};
+handle_event({call,From}, push, on, Data) ->
+ %% Go to 'off' and reply that the resulting status is 'off'
+ {next_state,off,Data,[{reply,From,off}]};
+%%
+%% Event handling common to all states
+handle_event({call,From}, get_count, State, Data) ->
+ %% Reply with the current count
+ {next_state,State,Data,[{reply,From,Data}]};
+handle_event(_, _, State, Data) ->
+ %% Ignore all other events
+ {next_state,State,Data}.
+ </code>
+ </section>
+
+ <datatypes>
+ <datatype>
+ <name name="server_name"/>
+ <desc>
+ <p>
+ Name specification to use when starting
+ a <c>gen_statem</c> server. See
+ <seealso marker="#start_link/3"><c>start_link/3</c></seealso>
+ and
+ <seealso marker="#type-server_ref"><c>server_ref()</c></seealso>
+ below.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="server_ref"/>
+ <desc>
+ <p>
+ Server specification to use when addressing
+ a <c>gen_statem</c> server.
+ See <seealso marker="#call/2"><c>call/2</c></seealso> and
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ above.
+ </p>
+ <p>It can be:</p>
+ <taglist>
+ <tag><c>pid() | LocalName</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> is locally registered.
+ </p>
+ </item>
+ <tag><c>{Name,Node}</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> is locally registered
+ on another node.
+ </p>
+ </item>
+ <tag><c>{global,GlobalName}</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> is globally registered in
+ <seealso marker="kernel:global"><c>kernel:global</c></seealso>.
+ </p>
+ </item>
+ <tag><c>{via,RegMod,ViaName}</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> is registered in
+ an alternative process registry.
+ The registry callback module <c>RegMod</c>
+ is to export functions
+ <c>register_name/2</c>, <c>unregister_name/1</c>,
+ <c>whereis_name/1</c>, and <c>send/2</c>,
+ which are to behave like the corresponding functions in
+ <seealso marker="kernel:global"><c>kernel:global</c></seealso>.
+ Thus, <c>{via,global,GlobalName}</c> is the same as
+ <c>{global,GlobalName}</c>.
+ </p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="debug_opt"/>
+ <desc>
+ <p>
+ Debug option that can be used when starting
+ a <c>gen_statem</c> server through, for example,
+ <seealso marker="#enter_loop/5"><c>enter_loop/5</c></seealso>.
+ </p>
+ <p>
+ For every entry in <c><anno>Dbgs</anno></c>,
+ the corresponding function in
+ <seealso marker="sys"><c>sys</c></seealso> is called.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="start_opt"/>
+ <desc>
+ <p>
+ Options that can be used when starting
+ a <c>gen_statem</c> server through, for example,
+ <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="start_ret"/>
+ <desc>
+ <p>
+ Return value from the start functions, for example,
+ <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="from"/>
+ <desc>
+ <p>
+ Destination to use when replying through, for example, the
+ <seealso marker="#type-action"><c>action()</c></seealso>
+ <c>{reply,From,Reply}</c>
+ to a process that has called the <c>gen_statem</c> server using
+ <seealso marker="#call/2"><c>call/2</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="state"/>
+ <desc>
+ <p>
+ After a state change (<c>NextState =/= State</c>),
+ all postponed events are retried.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="state_name"/>
+ <desc>
+ <p>
+ If the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ is <c>state_functions</c>,
+ the state must be of this type.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="data"/>
+ <desc>
+ <p>
+ A term in which the state machine implementation
+ is to store any server data it needs. The difference between
+ this and the <seealso marker="#type-state"><c>state()</c></seealso>
+ itself is that a change in this data does not cause
+ postponed events to be retried. Hence, if a change
+ in this data would change the set of events that
+ are handled, then that data item is to be made
+ a part of the state.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="event_type"/>
+ <desc>
+ <p>
+ External events are of three types:
+ <c>{call,<anno>From</anno>}</c>, <c>cast</c>, or <c>info</c>.
+ <seealso marker="#call/2">Calls</seealso>
+ (synchronous) and
+ <seealso marker="#cast/2">casts</seealso>
+ originate from the corresponding API functions.
+ For calls, the event contains whom to reply to.
+ Type <c>info</c> originates from regular process messages sent
+ to the <c>gen_statem</c>. Also, the state machine
+ implementation can generate events of types
+ <c>timeout</c> and <c>internal</c> to itself.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="callback_mode"/>
+ <desc>
+ <p>
+ The <em>callback mode</em> is selected when starting the
+ <c>gen_statem</c> using the return value from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ or when calling
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6,7</c></seealso>,
+ and with the return value from
+ <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>.
+ </p>
+ <taglist>
+ <tag><c>state_functions</c></tag>
+ <item>
+ <p>
+ The state must be of type
+ <seealso marker="#type-state_name"><c>state_name()</c></seealso>
+ and one callback function per state, that is,
+ <seealso marker="#Module:StateName/3"><c>Module:StateName/3</c></seealso>,
+ is used.
+ </p>
+ </item>
+ <tag><c>handle_event_function</c></tag>
+ <item>
+ <p>
+ The state can be any term and the callback function
+ <seealso marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>
+ is used for all states.
+ </p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="transition_option"/>
+ <desc>
+ <p>
+ Transition options can be set by
+ <seealso marker="#type-action">actions</seealso>
+ and they modify the following in how
+ the state transition is done:
+ </p>
+ <list type="ordered">
+ <item>
+ <p>
+ All
+ <seealso marker="#type-action">actions</seealso>
+ are processed in order of appearance.
+ </p>
+ </item>
+ <item>
+ <p>
+ If
+ <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ is <c>true</c>,
+ the current event is postponed.
+ </p>
+ </item>
+ <item>
+ <p>
+ If the state changes, the queue of incoming events
+ is reset to start with the oldest postponed.
+ </p>
+ </item>
+ <item>
+ <p>
+ All events stored with
+ <seealso marker="#type-action"><c>action()</c></seealso>
+ <c>next_event</c>
+ are inserted in the queue to be processed before
+ all other events.
+ </p>
+ </item>
+ <item>
+ <p>
+ If an
+ <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ is set through
+ <seealso marker="#type-action"><c>action()</c></seealso>
+ <c>timeout</c>,
+ an event timer can be started or a time-out zero event
+ can be enqueued.
+ </p>
+ </item>
+ <item>
+ <p>
+ The (possibly new)
+ <seealso marker="#state_function">state function</seealso>
+ is called with the oldest enqueued event if there is any,
+ otherwise the <c>gen_statem</c> goes into <c>receive</c>
+ or hibernation
+ (if
+ <seealso marker="#type-hibernate"><c>hibernate()</c></seealso>
+ is <c>true</c>)
+ to wait for the next message. In hibernation the next
+ non-system event awakens the <c>gen_statem</c>, or rather
+ the next incoming message awakens the <c>gen_statem</c>,
+ but if it is a system event
+ it goes right back into hibernation.
+ </p>
+ </item>
+ </list>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="postpone"/>
+ <desc>
+ <p>
+ If <c>true</c>, postpones the current event and retries
+ it when the state changes
+ (<c>NextState =/= State</c>).
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="hibernate"/>
+ <desc>
+ <p>
+ If <c>true</c>, hibernates the <c>gen_statem</c>
+ by calling
+ <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>
+ before going into <c>receive</c>
+ to wait for a new external event.
+ If there are enqueued events,
+ to prevent receiving any new event, an
+ <seealso marker="erts:erlang#garbage_collect/0"><c>erlang:garbage_collect/0</c></seealso>
+ is done instead to simulate
+ that the <c>gen_statem</c> entered hibernation
+ and immediately got awakened by the oldest enqueued event.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="event_timeout"/>
+ <desc>
+ <p>
+ Generates an event of
+ <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <c>timeout</c>
+ after this time (in milliseconds) unless another
+ event arrives in which case this time-out is cancelled.
+ Notice that a retried or inserted event
+ counts like a new in this respect.
+ </p>
+ <p>
+ If the value is <c>infinity</c>, no timer is started, as
+ it never triggers anyway.
+ </p>
+ <p>
+ If the value is <c>0</c>, the time-out event is immediately enqueued
+ unless there already are enqueued events, as the
+ time-out is then immediately cancelled.
+ This is a feature ensuring that a time-out <c>0</c> event
+ is processed before any not yet received external event.
+ </p>
+ <p>
+ Notice that it is not possible or needed to cancel this time-out,
+ as it is cancelled automatically by any other event.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="action"/>
+ <desc>
+ <p>
+ These state transition actions can be invoked by
+ returning them from the
+ <seealso marker="#state_function">state function</seealso>, from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ or by giving them to
+ <seealso marker="#enter_loop/6"><c>enter_loop/6,7</c></seealso>.
+ </p>
+ <p>
+ Actions are executed in the containing list order.
+ </p>
+ <p>
+ Actions that set
+ <seealso marker="#type-transition_option">transition options</seealso>
+ override any previous of the same type,
+ so the last in the containing list wins.
+ For example, the last
+ <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ overrides any other <c>event_timeout()</c> in the list.
+ </p>
+ <taglist>
+ <tag><c>postpone</c></tag>
+ <item>
+ <p>
+ Sets the
+ <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
+ <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ for this state transition.
+ This action is ignored when returned from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ or given to
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>,
+ as there is no event to postpone in those cases.
+ </p>
+ </item>
+ <tag><c>hibernate</c></tag>
+ <item>
+ <p>
+ Sets the
+ <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
+ <seealso marker="#type-hibernate"><c>hibernate()</c></seealso>
+ for this state transition.
+ </p>
+ </item>
+ <tag><c>Timeout</c></tag>
+ <item>
+ <p>
+ Short for <c>{timeout,Timeout,Timeout}</c>, that is,
+ the time-out message is the time-out time.
+ This form exists to make the
+ <seealso marker="#state_function">state function</seealso>
+ return value <c>{next_state,NextState,NewData,Timeout}</c>
+ allowed like for <c>gen_fsm</c>'s
+ <seealso marker="gen_fsm#Module:StateName/2"><c>Module:StateName/2</c></seealso>.
+ </p>
+ </item>
+ <tag><c>timeout</c></tag>
+ <item>
+ <p>
+ Sets the
+ <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
+ <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ to <c><anno>Time</anno></c> with <c><anno>EventContent</anno></c>.
+ </p>
+ </item>
+ <tag><c>reply_action()</c></tag>
+ <item>
+ <p>
+ Replies to a caller.
+ </p>
+ </item>
+ <tag><c>next_event</c></tag>
+ <item>
+ <p>
+ Stores the specified <c><anno>EventType</anno></c>
+ and <c><anno>EventContent</anno></c> for insertion after all
+ actions have been executed.
+ </p>
+ <p>
+ The stored events are inserted in the queue as the next to process
+ before any already queued events. The order of these stored events
+ is preserved, so the first <c>next_event</c> in the containing
+ list becomes the first to process.
+ </p>
+ <p>
+ An event of type
+ <seealso marker="#type-event_type"><c>internal</c></seealso>
+ is to be used when you want to reliably distinguish
+ an event inserted this way from any external event.
+ </p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="reply_action"/>
+ <desc>
+ <p>
+ Replies to a caller waiting for a reply in
+ <seealso marker="#call/2"><c>call/2</c></seealso>.
+ <c><anno>From</anno></c> must be the term from argument
+ <seealso marker="#type-event_type"><c>{call,<anno>From</anno>}</c></seealso>
+ to the
+ <seealso marker="#state_function">state function</seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="state_function_result"/>
+ <desc>
+ <taglist>
+ <tag><c>next_state</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> does a state transition to
+ <c><anno>NextStateName</anno></c>
+ (which can be the same as the current state),
+ sets <c><anno>NewData</anno></c>,
+ and executes all <c><anno>Actions</anno></c>.
+ </p>
+ </item>
+ </taglist>
+ <p>
+ All these terms are tuples or atoms and this property
+ will hold in any future version of <c>gen_statem</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="handle_event_result"/>
+ <desc>
+ <taglist>
+ <tag><c>next_state</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> does a state transition to
+ <c><anno>NextState</anno></c>
+ (which can be the same as the current state),
+ sets <c><anno>NewData</anno></c>,
+ and executes all <c><anno>Actions</anno></c>.
+ </p>
+ </item>
+ </taglist>
+ <p>
+ All these terms are tuples or atoms and this property
+ will hold in any future version of <c>gen_statem</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="common_state_callback_result"/>
+ <desc>
+ <taglist>
+ <tag><c>stop</c></tag>
+ <item>
+ <p>
+ Terminates the <c>gen_statem</c> by calling
+ <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ with <c>Reason</c> and
+ <c><anno>NewData</anno></c>, if specified.
+ </p>
+ </item>
+ <tag><c>stop_and_reply</c></tag>
+ <item>
+ <p>
+ Sends all <c><anno>Replies</anno></c>,
+ then terminates the <c>gen_statem</c> by calling
+ <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ with <c>Reason</c> and
+ <c><anno>NewData</anno></c>, if specified.
+ </p>
+ </item>
+ <tag><c>keep_state</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> keeps the current state, or
+ does a state transition to the current state if you like,
+ sets <c><anno>NewData</anno></c>,
+ and executes all <c><anno>Actions</anno></c>.
+ This is the same as
+ <c>{next_state,CurrentState,<anno>NewData</anno>,<anno>Actions</anno>}</c>.
+ </p>
+ </item>
+ <tag><c>keep_state_and_data</c></tag>
+ <item>
+ <p>
+ The <c>gen_statem</c> keeps the current state or
+ does a state transition to the current state if you like,
+ keeps the current server data,
+ and executes all <c><anno>Actions</anno></c>.
+ This is the same as
+ <c>{next_state,CurrentState,CurrentData,<anno>Actions</anno>}</c>.
+ </p>
+ </item>
+ </taglist>
+ <p>
+ All these terms are tuples or atoms and this property
+ will hold in any future version of <c>gen_statem</c>.
+ </p>
+ </desc>
+ </datatype>
+ </datatypes>
+
+ <funcs>
+ <func>
+ <name name="call" arity="2"/>
+ <name name="call" arity="3"/>
+ <fsummary>Make a synchronous call to a <c>gen_statem</c>.</fsummary>
+ <desc>
+ <p>
+ Makes a synchronous call to the <c>gen_statem</c>
+ <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+ by sending a request
+ and waiting until its reply arrives.
+ The <c>gen_statem</c> calls the
+ <seealso marker="#state_function">state function</seealso> with
+ <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <c>{call,From}</c> and event content
+ <c><anno>Request</anno></c>.
+ </p>
+ <p>
+ A <c><anno>Reply</anno></c> is generated when a
+ <seealso marker="#state_function">state function</seealso>
+ returns with
+ <c>{reply,From,<anno>Reply</anno>}</c> as one
+ <seealso marker="#type-action"><c>action()</c></seealso>,
+ and that <c><anno>Reply</anno></c> becomes the return value
+ of this function.
+ </p>
+ <p>
+ <c><anno>Timeout</anno></c> is an integer &gt; 0,
+ which specifies how many milliseconds to wait for a reply,
+ or the atom <c>infinity</c> to wait indefinitely,
+ which is the default. If no reply is received within
+ the specified time, the function call fails.
+ </p>
+ <note>
+ <p>
+ To avoid getting a late reply in the caller's
+ inbox, this function spawns a proxy process that
+ does the call. A late reply gets delivered to the
+ dead proxy process, hence gets discarded. This is
+ less efficient than using
+ <c><anno>Timeout</anno> =:= infinity</c>.
+ </p>
+ </note>
+ <p>
+ The call can fail, for example, if the <c>gen_statem</c> dies
+ before or during this function call.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="cast" arity="2"/>
+ <fsummary>Send an asynchronous event to a <c>gen_statem</c>.</fsummary>
+ <desc>
+ <p>
+ Sends an asynchronous event to the <c>gen_statem</c>
+ <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+ and returns <c>ok</c> immediately,
+ ignoring if the destination node or <c>gen_statem</c>
+ does not exist.
+ The <c>gen_statem</c> calls the
+ <seealso marker="#state_function">state function</seealso> with
+ <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <c>cast</c> and event content
+ <c><anno>Msg</anno></c>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="enter_loop" arity="5"/>
+ <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
+ <desc>
+ <p>
+ The same as
+ <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
+ except that no
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ must have been registered.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="enter_loop" arity="6"/>
+ <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
+ <desc>
+ <p>
+ If <c><anno>Server_or_Actions</anno></c> is a <c>list()</c>,
+ the same as
+ <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
+ except that no
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ must have been registered and
+ <c>Actions = <anno>Server_or_Actions</anno></c>.
+ </p>
+ <p>
+ Otherwise the same as
+ <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
+ with
+ <c>Server = <anno>Server_or_Actions</anno></c> and
+ <c>Actions = []</c>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="enter_loop" arity="7"/>
+ <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
+ <desc>
+ <p>
+ Makes the calling process become a <c>gen_statem</c>.
+ Does not return, instead the calling process enters
+ the <c>gen_statem</c> receive loop and becomes
+ a <c>gen_statem</c> server.
+ The process <em>must</em> have been started
+ using one of the start functions in
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>.
+ The user is responsible for any initialization of the process,
+ including registering a name for it.
+ </p>
+ <p>
+ This function is useful when a more complex initialization
+ procedure is needed than
+ the <c>gen_statem</c> behavior provides.
+ </p>
+ <p>
+ <c><anno>Module</anno></c>, <c><anno>Opts</anno></c>, and
+ <c><anno>Server</anno></c> have the same meanings
+ as when calling
+ <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ However, the
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ name must have been registered accordingly
+ <em>before</em> this function is called.</p>
+ <p>
+ <c><anno>CallbackMode</anno></c>, <c><anno>State</anno></c>,
+ <c><anno>Data</anno></c>, and <c><anno>Actions</anno></c>
+ have the same meanings as in the return value of
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.
+ Also, the callback module <c><anno>Module</anno></c>
+ does not need to export an <c>init/1</c> function.
+ </p>
+ <p>
+ The function fails if the calling process was not started by a
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ start function, or if it is not registered
+ according to
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="reply" arity="1"/>
+ <name name="reply" arity="2"/>
+ <fsummary>Reply to a caller.</fsummary>
+ <desc>
+ <p>
+ This function can be used by a <c>gen_statem</c>
+ to explicitly send a reply to a process that waits in
+ <seealso marker="#call/2"><c>call/2</c></seealso>
+ when the reply cannot be defined in
+ the return value of a
+ <seealso marker="#state_function">state function</seealso>.
+ </p>
+ <p>
+ <c><anno>From</anno></c> must be the term from argument
+ <seealso marker="#type-event_type"><c>{call,<anno>From</anno>}</c></seealso>
+ to the
+ <seealso marker="#state_function">state function</seealso>.
+ <c><anno>From</anno></c> and <c><anno>Reply</anno></c>
+ can also be specified using a
+ <seealso marker="#type-reply_action"><c>reply_action()</c></seealso>
+ and multiple replies with a list of them.
+ </p>
+ <note>
+ <p>
+ A reply sent with this function is not visible
+ in <seealso marker="sys"><c>sys</c></seealso> debug output.
+ </p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start" arity="3"/>
+ <name name="start" arity="4"/>
+ <fsummary>Create a standalone <c>gen_statem</c> process.</fsummary>
+ <desc>
+ <p>
+ Creates a standalone <c>gen_statem</c> process according to
+ OTP design principles (using
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ primitives).
+ As it does not get linked to the calling process,
+ this start function cannot be used by a supervisor
+ to start a child.
+ </p>
+ <p>
+ For a description of arguments and return values, see
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start_link" arity="3"/>
+ <name name="start_link" arity="4"/>
+ <fsummary>Create a linked <c>gen_statem</c> process.</fsummary>
+ <desc>
+ <p>
+ Creates a <c>gen_statem</c> process according
+ to OTP design principles
+ (using
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ primitives)
+ that is linked to the calling process.
+ This is essential when the <c>gen_statem</c> must be part of
+ a supervision tree so it gets linked to its supervisor.
+ </p>
+ <p>
+ The <c>gen_statem</c> process calls
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ to initialize the server. To ensure a synchronized startup
+ procedure, <c>start_link/3,4</c> does not return until
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ has returned.
+ </p>
+ <p>
+ <c><anno>ServerName</anno></c> specifies the
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ to register for the <c>gen_statem</c>.
+ If the <c>gen_statem</c> is started with <c>start_link/3</c>,
+ no <c><anno>ServerName</anno></c> is provided and
+ the <c>gen_statem</c> 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 that is passed as
+ the argument to
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>
+ If option <c>{timeout,Time}</c> is present in
+ <c><anno>Opts</anno></c>, the <c>gen_statem</c>
+ is allowed to spend <c>Time</c> milliseconds initializing
+ or it terminates and the start function returns
+ <seealso marker="#type-start_ret"><c>{error,timeout}</c></seealso>.
+ </p>
+ </item>
+ <item>
+ <p>
+ If option
+ <seealso marker="#type-debug_opt"><c>{debug,Dbgs}</c></seealso>
+ is present in <c><anno>Opts</anno></c>, debugging through
+ <seealso marker="sys"><c>sys</c></seealso> is activated.
+ </p>
+ </item>
+ <item>
+ <p>
+ If option <c>{spawn_opt,SpawnOpts}</c> is present in
+ <c><anno>Opts</anno></c>, <c>SpawnOpts</c> is passed
+ as option list to
+ <seealso marker="erts:erlang#spawn_opt/2"><c>erlang:spawn_opt/2</c></seealso>,
+ which is used to spawn the <c>gen_statem</c> process.
+ </p>
+ </item>
+ </list>
+ <note>
+ <p>
+ Using spawn option <c>monitor</c> is not
+ allowed, it causes this function to fail with reason
+ <c>badarg</c>.
+ </p>
+ </note>
+ <p>
+ If the <c>gen_statem</c> is successfully created
+ and initialized, this function returns
+ <seealso marker="#type-start_ret"><c>{ok,Pid}</c></seealso>,
+ where <c>Pid</c> is the <c>pid()</c>
+ of the <c>gen_statem</c>.
+ If a process with the specified <c><anno>ServerName</anno></c>
+ exists already, this function returns
+ <seealso marker="#type-start_ret"><c>{error,{already_started,Pid}}</c></seealso>,
+ where <c>Pid</c> is the <c>pid()</c> of that process.
+ </p>
+ <p>
+ If <c>Module:init/1</c> fails with <c>Reason</c>,
+ this function returns
+ <seealso marker="#type-start_ret"><c>{error,Reason}</c></seealso>.
+ If <c>Module:init/1</c> returns
+ <seealso marker="#type-start_ret"><c>{stop,Reason}</c></seealso>
+ or
+ <seealso marker="#type-start_ret"><c>ignore</c></seealso>,
+ the process is terminated and this function
+ returns
+ <seealso marker="#type-start_ret"><c>{error,Reason}</c></seealso>
+ or
+ <seealso marker="#type-start_ret"><c>ignore</c></seealso>,
+ respectively.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="stop" arity="1"/>
+ <fsummary>Synchronously stop a generic server.</fsummary>
+ <desc>
+ <p>
+ The same as
+ <seealso marker="#stop/3"><c>stop(<anno>ServerRef</anno>, normal, infinity)</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="stop" arity="3"/>
+ <fsummary>Synchronously stop a generic server.</fsummary>
+ <desc>
+ <p>
+ Orders the <c>gen_statem</c>
+ <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+ to exit with the specified <c><anno>Reason</anno></c>
+ and waits for it to terminate.
+ The <c>gen_statem</c> calls
+ <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ before exiting.
+ </p>
+ <p>
+ This function returns <c>ok</c> if the server terminates
+ with the expected reason. Any other reason than <c>normal</c>,
+ <c>shutdown</c>, or <c>{shutdown,Term}</c> causes an
+ error report to be issued through
+ <seealso marker="kernel:error_logger#format/2"><c>error_logger:format/2</c></seealso>.
+ The default <c><anno>Reason</anno></c> is <c>normal</c>.
+ </p>
+ <p>
+ <c><anno>Timeout</anno></c> is an integer &gt; 0,
+ which specifies how many milliseconds to wait for the server to
+ terminate, or the atom <c>infinity</c> to wait indefinitely.
+ Defaults to <c>infinity</c>.
+ If the server does not terminate within the specified time,
+ a <c>timeout</c> exception is raised.
+ </p>
+ <p>
+ If the process does not exist, a <c>noproc</c> exception
+ is raised.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>Callback Functions</title>
+ <p>
+ The following functions are to be exported from a
+ <c>gen_statem</c> callback module.
+ </p>
+ </section>
+
+ <funcs>
+ <func>
+ <name>Module:code_change(OldVsn, OldState, OldData, Extra) ->
+ Result
+ </name>
+ <fsummary>Update the internal state during upgrade/downgrade.</fsummary>
+ <type>
+ <v>OldVsn = Vsn | {down,Vsn}</v>
+ <v>&nbsp;&nbsp;Vsn = term()</v>
+ <v>OldState = NewState = term()</v>
+ <v>Extra = term()</v>
+ <v>Result = {NewCallbackMode,NewState,NewData} | Reason</v>
+ <v>
+ NewCallbackMode =
+ <seealso marker="#type-callback_mode">callback_mode()</seealso>
+ </v>
+ <v>
+ OldState = NewState =
+ <seealso marker="#type-state">state()</seealso>
+ </v>
+ <v>
+ OldData = NewData =
+ <seealso marker="#type-data">data()</seealso>
+ </v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is called by a <c>gen_statem</c> when it is to
+ update its internal state during a release upgrade/downgrade,
+ that is, when the instruction <c>{update,Module,Change,...}</c>,
+ where <c>Change={advanced,Extra}</c>, is specified in the
+ <seealso marker="sasl:appup"><c>appup</c></seealso>
+ file. For more information, see
+ <seealso marker="doc/design_principles:release_handling#instr">OTP Design Principles</seealso>.
+ </p>
+ <p>
+ For an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and
+ for a downgrade, <c>OldVsn</c> is
+ <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c>
+ attribute(s) of the old version of the callback module
+ <c>Module</c>. If no such attribute is defined, the version
+ is the checksum of the Beam file.
+ </p>
+ <note>
+ <p>
+ If you would dare to change
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ during release upgrade/downgrade, the upgrade is no problem,
+ as the new code surely knows what <em>callback mode</em>
+ it needs. However, for a downgrade this function must
+ know from argument <c>Extra</c> that comes from the
+ <seealso marker="sasl:appup"><c>sasl:appup</c></seealso>
+ file what <em>callback mode</em> the old code did use.
+ It can also be possible to figure this out
+ from argument <c>{down,Vsn}</c>, as <c>Vsn</c>
+ in effect defines the old callback module version.
+ </p>
+ </note>
+ <p>
+ <c>OldState</c> and <c>OldData</c> is the internal state
+ of the <c>gen_statem</c>.
+ </p>
+ <p>
+ <c>Extra</c> is passed "as is" from the <c>{advanced,Extra}</c>
+ part of the update instruction.
+ </p>
+ <p>
+ If successful, the function must return the updated
+ internal state in an
+ <c>{NewCallbackMode,NewState,NewData}</c> tuple.
+ </p>
+ <p>
+ If the function returns <c>Reason</c>, the ongoing
+ upgrade fails and rolls back to the old release.</p>
+ <p>
+ This function can use
+ <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
+ to return <c>Result</c> or <c>Reason</c>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:init(Args) -> Result</name>
+ <fsummary>Initialize process and internal state.</fsummary>
+ <type>
+ <v>Args = term()</v>
+ <v>Result = {CallbackMode,State,Data}</v>
+ <v>&nbsp;| {CallbackMode,State,Data,Actions}</v>
+ <v>&nbsp;| {stop,Reason} | ignore</v>
+ <v>
+ CallbackMode =
+ <seealso marker="#type-callback_mode">callback_mode()</seealso>
+ </v>
+ <v>State = <seealso marker="#type-state">state()</seealso></v>
+ <v>
+ Data = <seealso marker="#type-data">data()</seealso>
+ </v>
+ <v>
+ Actions =
+ [<seealso marker="#type-action">action()</seealso>] |
+ <seealso marker="#type-action">action()</seealso>
+ </v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <marker id="Module:init-1"/>
+ <p>
+ Whenever a <c>gen_statem</c> is started using
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>
+ or
+ <seealso marker="#start/3"><c>start/3,4</c></seealso>,
+ this function is called by the new process to initialize
+ the implementation state and server data.
+ </p>
+ <p>
+ <c>Args</c> is the <c>Args</c> argument provided to the start
+ function.
+ </p>
+ <p>
+ If the initialization is successful, the function is to
+ return <c>{CallbackMode,State,Data}</c> or
+ <c>{CallbackMode,State,Data,Actions}</c>.
+ <c>CallbackMode</c> selects the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ of the <c>gen_statem</c>.
+ <c>State</c> is the initial
+ <seealso marker="#type-state"><c>state()</c></seealso>
+ and <c>Data</c> the initial server
+ <seealso marker="#type-data"><c>data()</c></seealso>.
+ </p>
+ <p>
+ The <seealso marker="#type-action"><c>Actions</c></seealso>
+ are executed when entering the first
+ <seealso marker="#type-state">state</seealso> just as for a
+ <seealso marker="#state_function">state function</seealso>.
+ </p>
+ <p>
+ If the initialization fails,
+ the function is to return <c>{stop,Reason}</c>
+ or <c>ignore</c>; see
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ </p>
+ <p>
+ This function can use
+ <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
+ to return <c>Result</c>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:format_status(Opt, [PDict,State,Data]) ->
+ Status
+ </name>
+ <fsummary>Optional function for providing a term describing the
+ current <c>gen_statem</c> status.</fsummary>
+ <type>
+ <v>Opt = normal | terminate</v>
+ <v>PDict = [{Key, Value}]</v>
+ <v>
+ State =
+ <seealso marker="#type-state">state()</seealso>
+ </v>
+ <v>
+ Data =
+ <seealso marker="#type-data">data()</seealso>
+ </v>
+ <v>Key = term()</v>
+ <v>Value = term()</v>
+ <v>Status = term()</v>
+ </type>
+ <desc>
+ <note>
+ <p>
+ This callback is optional, so a callback module does not need
+ to export it. The <c>gen_statem</c> module provides a default
+ implementation of this function that returns
+ <c>{State,Data}</c>. If this callback fails, the default
+ function returns <c>{State,Info}</c>,
+ where <c>Info</c> informs of the crash but no details,
+ to hide possibly sensitive data.
+ </p>
+ </note>
+ <p>This function is called by a <c>gen_statem</c> process when
+ any of the following apply:</p>
+ <list type="bulleted">
+ <item>
+ One of
+ <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ is invoked to get the <c>gen_statem</c> status. <c>Opt</c> is set
+ to the atom <c>normal</c> for this case.
+ </item>
+ <item>
+ The <c>gen_statem</c> terminates abnormally and logs an error.
+ <c>Opt</c> is set to the atom <c>terminate</c> for this case.
+ </item>
+ </list>
+ <p>
+ This function is useful for changing the form and
+ appearance of the <c>gen_statem</c> status for these cases. A
+ callback module wishing to change the
+ <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ return value and how
+ its status appears in termination error logs exports an
+ instance of <c>format_status/2</c>, which returns a term
+ describing the current status of the <c>gen_statem</c>.
+ </p>
+ <p>
+ <c>PDict</c> is the current value of the process dictionary
+ of the <c>gen_statem</c>.
+ </p>
+ <p>
+ <seealso marker="#type-state"><c>State</c></seealso>
+ is the internal state of the <c>gen_statem</c>.
+ </p>
+ <p>
+ <seealso marker="#type-data"><c>Data</c></seealso>
+ is the internal server data of the <c>gen_statem</c>.
+ </p>
+ <p>
+ The function is to return <c>Status</c>, a term that
+ changes the details of the current state and status of
+ the <c>gen_statem</c>. There are no restrictions on the
+ form <c>Status</c> can take, but for the
+ <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ case (when <c>Opt</c>
+ is <c>normal</c>), the recommended form for
+ the <c>Status</c> value is <c>[{data, [{"State",
+ Term}]}]</c>, where <c>Term</c> provides relevant details of
+ the <c>gen_statem</c> state. Following this recommendation is not
+ required, but it makes the callback module status
+ consistent with the rest of the
+ <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ return value.
+ </p>
+ <p>
+ One use for this function is to return compact alternative
+ state representations to avoid having large state terms
+ printed in log files. Another use is to hide sensitive data from
+ being written to the error log.
+ </p>
+ <p>
+ This function can use
+ <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
+ to return <c>Status</c>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:StateName(EventType, EventContent, Data) ->
+ StateFunctionResult
+ </name>
+ <name>Module:handle_event(EventType, EventContent,
+ State, Data) -> HandleEventResult
+ </name>
+ <fsummary>Handle an event.</fsummary>
+ <type>
+ <v>
+ EventType =
+ <seealso marker="#type-event_type">event_type()</seealso>
+ </v>
+ <v>EventContent = term()</v>
+ <v>
+ State =
+ <seealso marker="#type-state">state()</seealso>
+ </v>
+ <v>
+ Data = NewData =
+ <seealso marker="#type-data">data()</seealso>
+ </v>
+ <v>
+ StateFunctionResult =
+ <seealso marker="#type-state_function_result">state_function_result()</seealso>
+ </v>
+ <v>
+ HandleEventResult =
+ <seealso marker="#type-handle_event_result">handle_event_result()</seealso>
+ </v>
+ </type>
+ <desc>
+ <p>
+ Whenever a <c>gen_statem</c> receives an event from
+ <seealso marker="#call/2"><c>call/2</c></seealso>,
+ <seealso marker="#cast/2"><c>cast/2</c></seealso>, or
+ as a normal process message, one of these functions is called. If
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ is <c>state_functions</c>, <c>Module:StateName/3</c> is called,
+ and if it is <c>handle_event_function</c>,
+ <c>Module:handle_event/4</c> is called.
+ </p>
+ <p>
+ If <c>EventType</c> is
+ <seealso marker="#type-event_type"><c>{call,From}</c></seealso>,
+ the caller waits for a reply. The reply can be sent
+ from this or from any other
+ <seealso marker="#state_function">state function</seealso>
+ by returning with <c>{reply,From,Reply}</c> in
+ <seealso marker="#type-action"><c>Actions</c></seealso>, in
+ <seealso marker="#type-reply_action"><c>Replies</c></seealso>,
+ or by calling
+ <seealso marker="#reply/2"><c>reply(From, Reply)</c></seealso>.
+ </p>
+ <p>
+ If this function returns with a next state that
+ does not match equal (<c>=/=</c>) to the current state,
+ all postponed events are retried in the next state.
+ </p>
+ <p>
+ The only difference between <c>StateFunctionResult</c> and
+ <c>HandleEventResult</c> is that for <c>StateFunctionResult</c>
+ the next state must be an atom, but for <c>HandleEventResult</c>
+ there is no restriction on the next state.
+ </p>
+ <p>
+ For options that can be set and actions that can be done
+ by <c>gen_statem</c> after returning from this function,
+ see <seealso marker="#type-action"><c>action()</c></seealso>.
+ </p>
+ <p>
+ These functions can use
+ <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>,
+ to return the result.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:terminate(Reason, State, Data) -> Ignored</name>
+ <fsummary>Clean up before termination.</fsummary>
+ <type>
+ <v>Reason = normal | shutdown | {shutdown,term()} | term()</v>
+ <v>State = <seealso marker="#type-state">state()</seealso></v>
+ <v>Data = <seealso marker="#type-data">data()</seealso></v>
+ <v>Ignored = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is called by a <c>gen_statem</c>
+ when it is about to terminate. It is to be the opposite of
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ and do any necessary cleaning up. When it returns,
+ the <c>gen_statem</c> terminates with <c>Reason</c>. The return
+ value is ignored.</p>
+ <p>
+ <c>Reason</c> is a term denoting the stop reason and
+ <seealso marker="#type-state"><c>State</c></seealso>
+ is the internal state of the <c>gen_statem</c>.
+ </p>
+ <p>
+ <c>Reason</c> depends on why the <c>gen_statem</c>
+ is terminating.
+ If it is because another callback function has returned, a
+ stop tuple <c>{stop,Reason}</c> in
+ <seealso marker="#type-action"><c>Actions</c></seealso>,
+ <c>Reason</c> has the value specified in that tuple.
+ If it is because of a failure, <c>Reason</c> is the error reason.
+ </p>
+ <p>
+ If the <c>gen_statem</c> is part of a supervision tree and is
+ ordered by its supervisor to terminate, this function is
+ called with <c>Reason = shutdown</c> if both the following
+ conditions apply:</p>
+ <list type="bulleted">
+ <item>
+ <p>
+ The <c>gen_statem</c> has been set
+ to trap exit signals.
+ </p>
+ </item>
+ <item>
+ <p>
+ The shutdown strategy as defined in the supervisor's
+ child specification is an integer time-out value, not
+ <c>brutal_kill</c>.
+ </p>
+ </item>
+ </list>
+ <p>
+ Even if the <c>gen_statem</c> is <em>not</em>
+ part of a supervision tree, this function is called
+ if it receives an <c>'EXIT'</c> message from its parent.
+ <c>Reason</c> is the same as
+ in the <c>'EXIT'</c> message.
+ </p>
+ <p>
+ Otherwise, the <c>gen_statem</c> is immediately terminated.
+ </p>
+ <p>
+ Notice that for any other reason than <c>normal</c>,
+ <c>shutdown</c>, or <c>{shutdown,Term}</c>,
+ the <c>gen_statem</c> is assumed to terminate because of an error
+ and an error report is issued using
+ <seealso marker="kernel:error_logger#format/2"><c>error_logger:format/2</c></seealso>.
+ </p>
+ <p>
+ This function can use
+ <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
+ to return <c>Ignored</c>, which is ignored anyway.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p>
+ <seealso marker="gen_event"><c>gen_event(3)</c></seealso>,
+ <seealso marker="gen_fsm"><c>gen_fsm(3)</c></seealso>,
+ <seealso marker="gen_server"><c>gen_server(3)</c></seealso>,
+ <seealso marker="proc_lib"><c>proc_lib(3)</c></seealso>,
+ <seealso marker="supervisor"><c>supervisor(3)</c></seealso>,
+ <seealso marker="sys"><c>sys(3)</c></seealso>.
+ </p>
+ </section>
+</erlref>
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index a8435efc6f..03d0063599 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -262,6 +262,21 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
+ <name name="join" arity="2"/>
+ <fsummary>Insert an element between elements in a list</fsummary>
+ <desc>
+ <p>Inserts <c><anno>Sep</anno></c> between each element in <c><anno>List1</anno></c>. Has no
+ effect on the empty list and on a singleton list. For example:</p>
+ <pre>
+> <input>lists:join(x, [a,b,c]).</input>
+[a,x,b,x,c]
+> <input>lists:join(x, [a]).</input>
+[a]
+> <input>lists:join(x, []).</input>
+[]</pre>
+ </desc>
+ </func>
+ <func>
<name name="foreach" arity="2"/>
<fsummary>Apply a function to each element of a list</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
index 0f58f19421..bf45461e2b 100644
--- a/lib/stdlib/doc/src/maps.xml
+++ b/lib/stdlib/doc/src/maps.xml
@@ -301,6 +301,30 @@ false</code>
</func>
<func>
+ <name name="take" 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 tuple with the removed <c><anno>Value</anno></c> and
+ the new map <c><anno>Map2</anno></c> without key <c><anno>Key</anno></c>.
+ If the key does not exist <c>error</c> is returned.
+ </p>
+ <p>
+ The call will fail with a <c>{badmap,Map}</c> exception if <c><anno>Map1</anno></c> is not a map.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"a" => "hello", "b" => "world"}.
+#{"a" => "hello", "b" => "world"}
+> maps:take("a",Map).
+{"hello",#{"b" => "world"}}
+> maps:take("does not exist",Map).
+error</code>
+ </desc>
+ </func>
+
+ <func>
<name name="size" arity="1"/>
<fsummary></fsummary>
<desc>
@@ -357,6 +381,42 @@ false</code>
</desc>
</func>
+ <func>
+ <name name="update_with" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>Update a value in a <c><anno>Map1</anno></c> associated with <c><anno>Key</anno></c> by
+ calling <c><anno>Fun</anno></c> on the old value to get a new value. An exception
+ <c>{badkey,<anno>Key</anno>}</c> is generated if
+ <c><anno>Key</anno></c> is not present in the map.</p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"counter" => 1},
+ Fun = fun(V) -> V + 1 end,
+ maps:update_with("counter",Fun,Map).
+#{"counter" => 2}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="update_with" arity="4"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>Update a value in a <c><anno>Map1</anno></c> associated with <c><anno>Key</anno></c> by
+ calling <c><anno>Fun</anno></c> on the old value to get a new value.
+ If <c><anno>Key</anno></c> is not present
+ in <c><anno>Map1</anno></c> then <c><anno>Init</anno></c> will be associated with
+ <c><anno>Key</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"counter" => 1},
+ Fun = fun(V) -> V + 1 end,
+ maps:update_with("new counter",Fun,42,Map).
+#{"counter" => 1,"new counter" => 42}</code>
+ </desc>
+ </func>
+
<func>
<name name="values" arity="1"/>
<fsummary></fsummary>
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index bef037076b..245580b1ba 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -34,9 +34,9 @@
<p>This module is used to start processes adhering to
the <seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso>. Specifically, the functions in this
module are used by the OTP standard behaviors (<c>gen_server</c>,
- <c>gen_fsm</c>, ...) when starting new processes. The functions
- can also be used to start <em>special processes</em>, user
- defined processes which comply to the OTP design principles. See
+ <c>gen_fsm</c>, <c>gen_statem</c>, ...) when starting new processes.
+ The functions can also be used to start <em>special processes</em>,
+ user defined processes which comply to the OTP design principles. See
<seealso marker="doc/design_principles:spec_proc">Sys and Proc_Lib</seealso> in OTP Design Principles for an example.</p>
<p>Some useful information is initialized when a process starts.
The registered names, or the process identifiers, of the parent
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml
index 82ad78e675..404873ea32 100644
--- a/lib/stdlib/doc/src/ref_man.xml
+++ b/lib/stdlib/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2015</year>
+ <year>1996</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -66,6 +66,7 @@
<xi:include href="gen_event.xml"/>
<xi:include href="gen_fsm.xml"/>
<xi:include href="gen_server.xml"/>
+ <xi:include href="gen_statem.xml"/>
<xi:include href="io.xml"/>
<xi:include href="io_lib.xml"/>
<xi:include href="lib.xml"/>
diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml
index 0418bf7b22..45b207b13d 100644
--- a/lib/stdlib/doc/src/specs.xml
+++ b/lib/stdlib/doc/src/specs.xml
@@ -30,6 +30,7 @@
<xi:include href="../specs/specs_gen_event.xml"/>
<xi:include href="../specs/specs_gen_fsm.xml"/>
<xi:include href="../specs/specs_gen_server.xml"/>
+ <xi:include href="../specs/specs_gen_statem.xml"/>
<xi:include href="../specs/specs_io.xml"/>
<xi:include href="../specs/specs_io_lib.xml"/>
<xi:include href="../specs/specs_lib.xml"/>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 421306283c..29e5a732d5 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -34,8 +34,8 @@
<p>A behaviour module for implementing a supervisor, a process which
supervises other processes called child processes. A child
process can either be another supervisor or a worker process.
- Worker processes are normally implemented using one of
- the <c>gen_event</c>, <c>gen_fsm</c>, or <c>gen_server</c>
+ Worker processes are normally implemented using one of the
+ <c>gen_event</c>, <c>gen_fsm</c>, <c>gen_statem</c> or <c>gen_server</c>
behaviours. A supervisor implemented using this module will have
a standard set of interface functions and include functionality
for tracing and error reporting. Supervisors are used to build a
@@ -221,7 +221,8 @@
<p><c>modules</c> is used by the release handler during code
replacement to determine which processes are using a certain
module. As a rule of thumb, if the child process is a
- <c>supervisor</c>, <c>gen_server</c>, or <c>gen_fsm</c>,
+ <c>supervisor</c>, <c>gen_server</c>,
+ <c>gen_fsm</c> or <c>gen_statem</c>
this should be a list with one element <c>[Module]</c>,
where <c>Module</c> is the callback module. If the child
process is an event manager (<c>gen_event</c>) with a
@@ -636,6 +637,7 @@
<title>SEE ALSO</title>
<p><seealso marker="gen_event">gen_event(3)</seealso>,
<seealso marker="gen_fsm">gen_fsm(3)</seealso>,
+ <seealso marker="gen_statem">gen_statem(3)</seealso>,
<seealso marker="gen_server">gen_server(3)</seealso>,
<seealso marker="sys">sys(3)</seealso></p>
</section>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index d80ec4dfa6..2255395f46 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -217,14 +217,18 @@
processes. For example, a <c>gen_server</c> process returns
the callback module's state, a <c>gen_fsm</c> process
returns information such as its current state name and state data,
- and a <c>gen_event</c> process returns information about each of its
+ a <c>gen_statem</c> process returns information about
+ its current state and data, and a <c>gen_event</c> process
+ returns information about each of its
registered handlers. Callback modules for <c>gen_server</c>,
- <c>gen_fsm</c>, and <c>gen_event</c> can also customise the value
+ <c>gen_fsm</c>, <c>gen_statem</c> and <c>gen_event</c>
+ can also customise the value
of <c><anno>Misc</anno></c> by exporting a <c>format_status/2</c>
function that contributes module-specific information;
- see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso>,
- <seealso marker="gen_fsm#Module:format_status/2">gen_fsm:format_status/2</seealso>, and
- <seealso marker="gen_event#Module:format_status/2">gen_event:format_status/2</seealso>
+ see <seealso marker="gen_server#Module:format_status/2">gen_server format_status/2</seealso>,
+ <seealso marker="gen_fsm#Module:format_status/2">gen_fsm format_status/2</seealso>,
+ <seealso marker="gen_statem#Module:format_status/2">gen_statem format_status/2</seealso>, and
+ <seealso marker="gen_event#Module:format_status/2">gen_event format_status/2</seealso>
for more details.</p>
</desc>
</func>
@@ -245,6 +249,8 @@
processes. For a <c>gen_server</c> process, the returned <c><anno>State</anno></c>
is simply the callback module's state. For a <c>gen_fsm</c> process,
<c><anno>State</anno></c> is the tuple <c>{CurrentStateName, CurrentStateData}</c>.
+ For a <c>gen_statem</c> process <c><anno>State</anno></c> is
+ the tuple <c>{CurrentState,CurrentData}.</c>
For a <c>gen_event</c> process, <c><anno>State</anno></c> a list of tuples,
where each tuple corresponds to an event handler registered in the process and contains
<c>{Module, Id, HandlerState}</c>, where <c>Module</c> is the event handler's module name,
@@ -263,8 +269,9 @@
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
+ processes</seealso>. The <c>gen_server</c>, <c>gen_fsm</c>,
+ <c>gen_statem</c> and <c>gen_event</c> OTP
+ behaviour modules export this function, 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
@@ -290,6 +297,8 @@
<c>gen_fsm</c> process, <c><anno>State</anno></c> is the tuple
<c>{CurrentStateName, CurrentStateData}</c>, and <c><anno>NewState</anno></c>
is a similar tuple that may contain a new state name, new state data, or both.
+ The same applies for a <c>gen_statem</c> process but
+ it names the tuple fields <c>{CurrentState,CurrentData}</c>.
For a <c>gen_event</c> process, <c><anno>State</anno></c> is the tuple
<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
@@ -304,7 +313,8 @@
state, then regardless of process type, it may simply return its <c><anno>State</anno></c>
argument.</p>
<p>If a <c><anno>StateFun</anno></c> function crashes or throws an exception, then
- for <c>gen_server</c> and <c>gen_fsm</c> processes, the original state of the process is
+ for <c>gen_server</c>, <c>gen_fsm</c> or <c>gen_statem</c> processes,
+ the original state of the process is
unchanged. For <c>gen_event</c> processes, a crashing or failing <c><anno>StateFun</anno></c>
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
@@ -329,7 +339,8 @@
<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,
+ <c>gen_server</c>, <c>gen_fsm</c>, <c>gen_statem</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>
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index c2e345763a..eab2ec4164 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -671,6 +671,10 @@ map_pair_types([{type,Line,map_field_assoc,[K,V]}|Ps]) ->
K1 = type(K),
V1 = type(V),
[{type,Line,map_field_assoc,[K1,V1]}|map_pair_types(Ps)];
+map_pair_types([{type,Line,map_field_exact,[K,V]}|Ps]) ->
+ K1 = type(K),
+ V1 = type(V),
+ [{type,Line,map_field_exact,[K1,V1]}|map_pair_types(Ps)];
map_pair_types([]) -> [].
field_types([{type,Line,field_type,[{atom,La,A},T]}|Fs]) ->
diff --git a/lib/stdlib/include/assert.hrl b/lib/stdlib/include/assert.hrl
index 151794d114..9e5d4eb598 100644
--- a/lib/stdlib/include/assert.hrl
+++ b/lib/stdlib/include/assert.hrl
@@ -59,19 +59,22 @@
-define(assert(BoolExpr),ok).
-else.
%% The assert macro is written the way it is so as not to cause warnings
-%% for clauses that cannot match, even if the expression is a constant.
+%% for clauses that cannot match, even if the expression is a constant or
+%% is known to be boolean-only.
-define(assert(BoolExpr),
begin
((fun () ->
+ __T = is_process_alive(self()), % cheap source of truth
case (BoolExpr) of
- true -> ok;
+ __T -> ok;
__V -> erlang:error({assert,
[{module, ?MODULE},
{line, ?LINE},
{expression, (??BoolExpr)},
{expected, true},
- case __V of false -> {value, __V};
- _ -> {not_boolean,__V}
+ case not __T of
+ __V -> {value, false};
+ _ -> {not_boolean, __V}
end]})
end
end)())
@@ -85,15 +88,17 @@
-define(assertNot(BoolExpr),
begin
((fun () ->
+ __F = not is_process_alive(self()),
case (BoolExpr) of
- false -> ok;
+ __F -> ok;
__V -> erlang:error({assert,
[{module, ?MODULE},
{line, ?LINE},
{expression, (??BoolExpr)},
{expected, false},
- case __V of true -> {value, __V};
- _ -> {not_boolean,__V}
+ case not __F of
+ __V -> {value, true};
+ _ -> {not_boolean, __V}
end]})
end
end)())
@@ -149,7 +154,8 @@
-else.
-define(assertEqual(Expect, Expr),
begin
- ((fun (__X) ->
+ ((fun () ->
+ __X = (Expect),
case (Expr) of
__X -> ok;
__V -> erlang:error({assertEqual,
@@ -159,7 +165,7 @@
{expected, __X},
{value, __V}]})
end
- end)(Expect))
+ end)())
end).
-endif.
@@ -169,7 +175,8 @@
-else.
-define(assertNotEqual(Unexpected, Expr),
begin
- ((fun (__X) ->
+ ((fun () ->
+ __X = (Unexpected),
case (Expr) of
__X -> erlang:error({assertNotEqual,
[{module, ?MODULE},
@@ -178,7 +185,7 @@
{value, __X}]});
_ -> ok
end
- end)(Unexpected))
+ end)())
end).
-endif.
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 9f4a446ea0..302834f9d0 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2015. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -85,6 +85,7 @@ MODULES= \
gen_event \
gen_fsm \
gen_server \
+ gen_statem \
io \
io_lib \
io_lib_format \
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 936c095aef..73934e0e3c 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -43,13 +43,18 @@
-type name() :: atom().
-type argspec() :: 'none' %No arguments
| non_neg_integer(). %Number of arguments
+-type argnames() :: [atom()].
-type tokens() :: [erl_scan:token()].
+-type predef() :: 'undefined' | {'none', tokens()}.
+-type userdef() :: {argspec(), {argnames(), tokens()}}.
-type used() :: {name(), argspec()}.
-type function_name_type() :: 'undefined'
| {atom(),non_neg_integer()}
| tokens().
+-type warning_info() :: {erl_anno:location(), module(), term()}.
+
-define(DEFAULT_ENCODING, utf8).
%% Epp state record.
@@ -63,7 +68,7 @@
sstk=[] :: [#epp{}], %State stack
path=[] :: [file:name()], %Include-path
macs = #{} %Macros (don't care locations)
- :: #{name() => {argspec(), tokens()}},
+ :: #{name() => predef() | [userdef()]},
uses = #{} %Macro use structure
:: #{name() => [{argspec(), [used()]}]},
default_encoding = ?DEFAULT_ENCODING :: source_encoding(),
@@ -155,11 +160,13 @@ scan_erl_form(Epp) ->
epp_request(Epp, scan_erl_form).
-spec parse_erl_form(Epp) ->
- {'ok', AbsForm} | {'eof', Line} | {error, ErrorInfo} when
+ {'ok', AbsForm} | {error, ErrorInfo} |
+ {'warning',WarningInfo} | {'eof',Line} when
Epp :: epp_handle(),
AbsForm :: erl_parse:abstract_form(),
Line :: erl_anno:line(),
- ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
+ WarningInfo :: warning_info().
parse_erl_form(Epp) ->
case epp_request(Epp, scan_erl_form) of
@@ -216,6 +223,10 @@ format_error({illegal_function_usage,Macro}) ->
io_lib:format("?~s must not begin a form", [Macro]);
format_error({'NYI',What}) ->
io_lib:format("not yet implemented '~s'", [What]);
+format_error({error,Term}) ->
+ io_lib:format("-error(~p).", [Term]);
+format_error({warning,Term}) ->
+ io_lib:format("-warning(~p).", [Term]);
format_error(E) -> file:format_error(E).
-spec parse_file(FileName, IncludePath, PredefMacros) ->
@@ -260,9 +271,11 @@ parse_file(Ifile, Options) ->
-spec parse_file(Epp) -> [Form] when
Epp :: epp_handle(),
- Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
+ Form :: erl_parse:abstract_form() | {'error', ErrorInfo} |
+ {'warning',WarningInfo} | {'eof',Line},
Line :: erl_anno:line(),
- ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
+ WarningInfo :: warning_info().
parse_file(Epp) ->
case parse_erl_form(Epp) of
@@ -270,6 +283,8 @@ parse_file(Epp) ->
[Form|parse_file(Epp)];
{error,E} ->
[{error,E}|parse_file(Epp)];
+ {warning,W} ->
+ [{warning,W}|parse_file(Epp)];
{eof,Location} ->
[{eof,erl_anno:new(Location)}]
end.
@@ -749,6 +764,10 @@ scan_toks([{'-',_Lh},{atom,_Ld,define}=Define|Toks], From, St) ->
scan_define(Toks, Define, From, St);
scan_toks([{'-',_Lh},{atom,_Ld,undef}=Undef|Toks], From, St) ->
scan_undef(Toks, Undef, From, St);
+scan_toks([{'-',_Lh},{atom,_Ld,error}=Error|Toks], From, St) ->
+ scan_err_warn(Toks, Error, From, St);
+scan_toks([{'-',_Lh},{atom,_Ld,warning}=Warn|Toks], From, St) ->
+ scan_err_warn(Toks, Warn, From, St);
scan_toks([{'-',_Lh},{atom,_Li,include}=Inc|Toks], From, St) ->
scan_include(Toks, Inc, From, St);
scan_toks([{'-',_Lh},{atom,_Li,include_lib}=IncLib|Toks], From, St) ->
@@ -804,6 +823,24 @@ scan_extends([{atom,Ln,A}=ModAtom,{')',_Lr}|_Ts], Ms0) ->
Ms#{'BASE_MODULE_STRING':={none,[{string,Ln,ModString}]}};
scan_extends(_Ts, Ms) -> Ms.
+scan_err_warn([{'(',_}|_]=Toks0, {atom,_,Tag}=Token, From, St) ->
+ try expand_macros(Toks0, St) of
+ Toks when is_list(Toks) ->
+ case erl_parse:parse_term(Toks) of
+ {ok,Term} ->
+ epp_reply(From, {Tag,{loc(Token),epp,{Tag,Term}}});
+ {error,_} ->
+ epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}})
+ end
+ catch
+ _:_ ->
+ epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}})
+ end,
+ wait_req_scan(St);
+scan_err_warn(_Toks, {atom,_,Tag}=Token, From, St) ->
+ epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}}),
+ wait_req_scan(St).
+
%% scan_define(Tokens, DefineToken, From, EppState)
scan_define([{'(',_Lp},{Type,_Lm,_}=Mac|Toks], Def, From, St)
@@ -930,9 +967,15 @@ scan_include(_Toks, Inc, From, St) ->
%% normal search path, if not we assume that the first directory name
%% is a library name, find its true directory and try with that.
-find_lib_dir(NewName) ->
- [Lib | Rest] = filename:split(NewName),
- {code:lib_dir(list_to_atom(Lib)), Rest}.
+expand_lib_dir(Name) ->
+ try
+ [App|Path] = filename:split(Name),
+ LibDir = code:lib_dir(list_to_atom(App)),
+ {ok,fname_join([LibDir|Path])}
+ catch
+ _:_ ->
+ error
+ end.
scan_include_lib([{'(',_Llp},{string,_Lf,_NewName0},{')',_Lrp},{dot,_Ld}],
Inc, From, St)
@@ -947,12 +990,11 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
{ok,NewF,Pname} ->
wait_req_scan(enter_file2(NewF, Pname, From, St, Loc));
{error,_E1} ->
- case catch find_lib_dir(NewName) of
- {LibDir, Rest} when is_list(LibDir) ->
- LibName = fname_join([LibDir | Rest]),
- case file:open(LibName, [read]) of
+ case expand_lib_dir(NewName) of
+ {ok,Header} ->
+ case file:open(Header, [read]) of
{ok,NewF} ->
- wait_req_scan(enter_file2(NewF, LibName, From,
+ wait_req_scan(enter_file2(NewF, Header, From,
St, Loc));
{error,_E2} ->
epp_reply(From,
@@ -960,7 +1002,7 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
{include,lib,NewName}}}),
wait_req_scan(St)
end;
- _Error ->
+ error ->
epp_reply(From, {error,{loc(Inc),epp,
{include,lib,NewName}}}),
wait_req_scan(St)
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index b14102ac38..2508f96b91 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -1502,7 +1502,7 @@ pattern({op,_Line,'++',{string,_Li,_S},R}, Vt, Old, Bvt, St) ->
pattern({match,_Line,Pat1,Pat2}, Vt, Old, Bvt, St0) ->
{Lvt,Bvt1,St1} = pattern(Pat1, Vt, Old, Bvt, St0),
{Rvt,Bvt2,St2} = pattern(Pat2, Vt, Old, Bvt, St1),
- St3 = reject_bin_alias(Pat1, Pat2, St2),
+ St3 = reject_invalid_alias(Pat1, Pat2, Vt, St2),
{vtmerge_pat(Lvt, Rvt),vtmerge_pat(Bvt1,Bvt2),St3};
%% Catch legal constant expressions, including unary +,-.
pattern(Pat, _Vt, _Old, _Bvt, St) ->
@@ -1517,56 +1517,77 @@ pattern_list(Ps, Vt, Old, Bvt0, St) ->
{vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt,Bvt1),St1}
end, {[],[],St}, Ps).
-%% reject_bin_alias(Pat, Expr, St) -> St'
+
+
+%% reject_invalid_alias(Pat, Expr, Vt, St) -> St'
%% Reject aliases for binary patterns at the top level.
+%% Reject aliases for maps patterns at the top level.
+%% The variables table (Vt) are for maps checkking.
+
+reject_invalid_alias_expr({bin,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr({map,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr({match,_,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr(_, _, _, St) -> St.
-reject_bin_alias_expr({bin,_,_}=P, {match,_,P0,E}, St0) ->
- St = reject_bin_alias(P, P0, St0),
- reject_bin_alias_expr(P, E, St);
-reject_bin_alias_expr({match,_,_,_}=P, {match,_,P0,E}, St0) ->
- St = reject_bin_alias(P, P0, St0),
- reject_bin_alias_expr(P, E, St);
-reject_bin_alias_expr(_, _, St) -> St.
-%% reject_bin_alias(Pat1, Pat2, St) -> St'
+%% reject_invalid_alias(Pat1, Pat2, St) -> St'
%% Aliases of binary patterns, such as <<A:8>> = <<B:4,C:4>> or even
%% <<A:8>> = <<A:8>>, are not allowed. Traverse the patterns in parallel
%% and generate an error if any binary aliases are found.
%% We generate an error even if is obvious that the overall pattern can't
%% possibly match, for instance, {a,<<A:8>>,c}={x,<<A:8>>} WILL generate an
%% error.
+%% Maps should reject unbound variables here.
-reject_bin_alias({bin,Line,_}, {bin,_,_}, St) ->
+reject_invalid_alias({bin,Line,_}, {bin,_,_}, _, St) ->
add_error(Line, illegal_bin_pattern, St);
-reject_bin_alias({cons,_,H1,T1}, {cons,_,H2,T2}, St0) ->
- St = reject_bin_alias(H1, H2, St0),
- reject_bin_alias(T1, T2, St);
-reject_bin_alias({tuple,_,Es1}, {tuple,_,Es2}, St) ->
- reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2},
+reject_invalid_alias({map,_Line,Ps1}, {map,_,Ps2}, Vt, St0) ->
+ Fun = fun ({map_field_exact,L,{var,_,K},_V}, Sti) ->
+ case is_var_bound(K,Vt) of
+ true ->
+ Sti;
+ false ->
+ add_error(L, {unbound_var,K}, Sti)
+ end;
+ ({map_field_exact,_L,_K,_V}, Sti) ->
+ Sti
+ end,
+ foldl(Fun, foldl(Fun, St0, Ps1), Ps2);
+reject_invalid_alias({cons,_,H1,T1}, {cons,_,H2,T2}, Vt, St0) ->
+ St = reject_invalid_alias(H1, H2, Vt, St0),
+ reject_invalid_alias(T1, T2, Vt, St);
+reject_invalid_alias({tuple,_,Es1}, {tuple,_,Es2}, Vt, St) ->
+ reject_invalid_alias_list(Es1, Es2, Vt, St);
+reject_invalid_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2}, Vt,
#lint{records=Recs}=St) ->
case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of
{{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} ->
- reject_bin_alias_rec(Pfs1, Pfs2, Fields1, Fields2, St);
+ reject_invalid_alias_rec(Pfs1, Pfs2, Fields1, Fields2, Vt, St);
{_,_} ->
%% One or more non-existing records. (An error messages has
%% already been generated, so we are done here.)
St
end;
-reject_bin_alias({match,_,P1,P2}, P, St0) ->
- St = reject_bin_alias(P1, P, St0),
- reject_bin_alias(P2, P, St);
-reject_bin_alias(P, {match,_,_,_}=M, St) ->
- reject_bin_alias(M, P, St);
-reject_bin_alias(_P1, _P2, St) -> St.
-
-reject_bin_alias_list([E1|Es1], [E2|Es2], St0) ->
- St = reject_bin_alias(E1, E2, St0),
- reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias_list(_, _, St) -> St.
-
-reject_bin_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, St) ->
+reject_invalid_alias({match,_,P1,P2}, P, Vt, St0) ->
+ St = reject_invalid_alias(P1, P, Vt, St0),
+ reject_invalid_alias(P2, P, Vt, St);
+reject_invalid_alias(P, {match,_,_,_}=M, Vt, St) ->
+ reject_invalid_alias(M, P, Vt, St);
+reject_invalid_alias(_P1, _P2, _Vt, St) -> St.
+
+reject_invalid_alias_list([E1|Es1], [E2|Es2], Vt, St0) ->
+ St = reject_invalid_alias(E1, E2, Vt, St0),
+ reject_invalid_alias_list(Es1, Es2, Vt, St);
+reject_invalid_alias_list(_, _, _, St) -> St.
+
+reject_invalid_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, Vt, St) ->
%% We treat records as if they have been converted to tuples.
PfsA1 = rbia_field_vars(PfsA0),
PfsB1 = rbia_field_vars(PfsB0),
@@ -1582,7 +1603,7 @@ reject_bin_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, St) ->
D = sofs:projection({external,fun({_,_,P1,_,P2}) -> {P1,P2} end}, C),
E = sofs:to_external(D),
{Ps1,Ps2} = lists:unzip(E),
- reject_bin_alias_list(Ps1, Ps2, St).
+ reject_invalid_alias_list(Ps1, Ps2, Vt, St).
rbia_field_vars(Fs) ->
[{Name,Pat} || {record_field,_,{atom,_,Name},Pat} <- Fs].
@@ -2284,7 +2305,7 @@ expr({'catch',Line,E}, Vt, St0) ->
expr({match,_Line,P,E}, Vt, St0) ->
{Evt,St1} = expr(E, Vt, St0),
{Pvt,Bvt,St2} = pattern(P, vtupdate(Evt, Vt), St1),
- St = reject_bin_alias_expr(P, E, St2),
+ St = reject_invalid_alias_expr(P, E, Vt, St2),
{vtupdate(Bvt, vtmerge(Evt, Pvt)),St};
%% No comparison or boolean operators yet.
expr({op,_Line,_Op,A}, Vt, St) ->
@@ -2381,7 +2402,7 @@ is_valid_call(Call) ->
_ -> true
end.
-%% is_valid_map_key(K,St) -> true | false
+%% is_valid_map_key(K) -> true | false
%% variables are allowed for patterns only at the top of the tree
is_valid_map_key({var,_,_}) -> true;
@@ -3413,6 +3434,14 @@ warn_unused_vars(U, Vt, St0) ->
UVt = map(fun ({V,{State,_,Ls}}) -> {V,{State,used,Ls}} end, U),
{vtmerge(Vt, UVt), St1}.
+
+is_var_bound(V, Vt) ->
+ case orddict:find(V, Vt) of
+ {ok,{bound,_Usage,_}} -> true;
+ _ -> false
+ end.
+
+
%% vtupdate(UpdVarTable, VarTable) -> VarTable.
%% Add the variables in the updated vartable to VarTable. The variables
%% will be updated with their property in UpdVarTable. The state of
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 6f8e5e8449..a896de4f1c 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -170,9 +170,16 @@ fun_type -> '(' top_types ')' '->' top_type
: {type, ?anno('$1'), 'fun',
[{type, ?anno('$1'), product, '$2'},'$5']}.
+map_pair_types -> '...' : [{type, ?anno('$1'), map_field_assoc,
+ [{type, ?anno('$1'), any, []},
+ {type, ?anno('$1'), any, []}]}].
map_pair_types -> map_pair_type : ['$1'].
map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3'].
-map_pair_type -> top_type '=>' top_type : {type, ?anno('$2'), map_field_assoc,['$1','$3']}.
+
+map_pair_type -> top_type '=>' top_type : {type, ?anno('$2'),
+ map_field_assoc,['$1','$3']}.
+map_pair_type -> top_type ':=' top_type : {type, ?anno('$2'),
+ map_field_exact,['$1','$3']}.
field_types -> field_type : ['$1'].
field_types -> field_type ',' field_types : ['$1'|'$3'].
@@ -810,7 +817,8 @@ Erlang code.
| {'type', anno(), 'map', [af_map_pair_type()]}.
-type af_map_pair_type() ::
- {'type', anno(), 'map_field_assoc', [abstract_type()]}.
+ {'type', anno(), 'map_field_assoc', [abstract_type()]}
+ | {'type', anno(), 'map_field_exact', [abstract_type()]}.
-type af_predefined_type() ::
{'type', anno(), type_name(), [abstract_type()]}.
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index c5177aca90..ca764675fc 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -344,11 +344,31 @@ binary_type(I1, I2) ->
map_type(Fs) ->
{first,[$#],map_pair_types(Fs)}.
-map_pair_types(Fs) ->
+map_pair_types(Fs0) ->
+ Fs = replace_any_map(Fs0),
tuple_type(Fs, fun map_pair_type/2).
+replace_any_map([{type,Line,map_field_assoc,[KType,VType]}]=Fs) ->
+ IsAny = fun({type,_,any,[]}) -> true;
+ %% ({var,_,'_'}) -> true;
+ (_) -> false
+ end,
+ case IsAny(KType) andalso IsAny(VType) of
+ true ->
+ [{type,Line,map_field_assoc,any}];
+ false ->
+ Fs
+ end;
+replace_any_map([F|Fs]) ->
+ [F|replace_any_map(Fs)];
+replace_any_map([]) -> [].
+
+map_pair_type({type,_Line,map_field_assoc,any}, _Prec) ->
+ leaf("...");
map_pair_type({type,_Line,map_field_assoc,[KType,VType]}, Prec) ->
- {list,[{cstep,[ltype(KType, Prec),leaf(" =>")],ltype(VType, Prec)}]}.
+ {list,[{cstep,[ltype(KType, Prec),leaf(" =>")],ltype(VType, Prec)}]};
+map_pair_type({type,_Line,map_field_exact,[KType,VType]}, Prec) ->
+ {list,[{cstep,[ltype(KType, Prec),leaf(" :=")],ltype(VType, Prec)}]}.
record_type(Name, Fields) ->
{first,[record_name(Name)],field_types(Fields)}.
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 9811a211ae..597830cf9a 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -26,7 +26,8 @@
%%%
%%% The standard behaviour should export init_it/6.
%%%-----------------------------------------------------------------
--export([start/5, start/6, debug_options/1,
+-export([start/5, start/6, debug_options/2,
+ name/1, unregister_name/1, get_proc_name/1, get_parent/0,
call/3, call/4, reply/2, stop/1, stop/3]).
-export([init_it/6, init_it/7]).
@@ -124,7 +125,7 @@ init_it(GenMod, Starter, Parent, Mod, Args, Options) ->
init_it2(GenMod, Starter, Parent, self(), Mod, Args, Options).
init_it(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
- case name_register(Name) of
+ case register_name(Name) of
true ->
init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options);
{false, Pid} ->
@@ -297,19 +298,19 @@ where({global, Name}) -> global:whereis_name(Name);
where({via, Module, Name}) -> Module:whereis_name(Name);
where({local, Name}) -> whereis(Name).
-name_register({local, Name} = LN) ->
+register_name({local, Name} = LN) ->
try register(Name, self()) of
true -> true
catch
error:_ ->
{false, where(LN)}
end;
-name_register({global, Name} = GN) ->
+register_name({global, Name} = GN) ->
case global:register_name(Name, self()) of
yes -> true;
no -> {false, where(GN)}
end;
-name_register({via, Module, Name} = GN) ->
+register_name({via, Module, Name} = GN) ->
case Module:register_name(Name, self()) of
yes ->
true;
@@ -317,34 +318,108 @@ name_register({via, Module, Name} = GN) ->
{false, where(GN)}
end.
+name({local,Name}) -> Name;
+name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
+name(Pid) when is_pid(Pid) -> Pid.
+
+unregister_name({local,Name}) ->
+ try unregister(Name) of
+ _ -> ok
+ catch
+ _:_ -> ok
+ end;
+unregister_name({global,Name}) ->
+ _ = global:unregister_name(Name),
+ ok;
+unregister_name({via, Mod, Name}) ->
+ _ = Mod:unregister_name(Name),
+ ok;
+unregister_name(Pid) when is_pid(Pid) ->
+ ok.
+
+get_proc_name(Pid) when is_pid(Pid) ->
+ Pid;
+get_proc_name({local, Name}) ->
+ case process_info(self(), registered_name) of
+ {registered_name, Name} ->
+ Name;
+ {registered_name, _Name} ->
+ exit(process_not_registered);
+ [] ->
+ exit(process_not_registered)
+ end;
+get_proc_name({global, Name}) ->
+ case global:whereis_name(Name) of
+ undefined ->
+ exit(process_not_registered_globally);
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit(process_not_registered_globally)
+ end;
+get_proc_name({via, Mod, Name}) ->
+ case Mod:whereis_name(Name) of
+ undefined ->
+ exit({process_not_registered_via, Mod});
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit({process_not_registered_via, Mod})
+ end.
+
+get_parent() ->
+ case get('$ancestors') of
+ [Parent | _] when is_pid(Parent) ->
+ Parent;
+ [Parent | _] when is_atom(Parent) ->
+ name_to_pid(Parent);
+ _ ->
+ exit(process_was_not_started_by_proc_lib)
+ end.
+
+name_to_pid(Name) ->
+ case whereis(Name) of
+ undefined ->
+ case global:whereis_name(Name) of
+ undefined ->
+ exit(could_not_find_registered_name);
+ Pid ->
+ Pid
+ end;
+ Pid ->
+ Pid
+ end.
+
timeout(Options) ->
- case opt(timeout, Options) of
- {ok, Time} ->
+ case lists:keyfind(timeout, 1, Options) of
+ {_,Time} ->
Time;
- _ ->
+ false ->
infinity
end.
spawn_opts(Options) ->
- case opt(spawn_opt, Options) of
- {ok, Opts} ->
+ case lists:keyfind(spawn_opt, 1, Options) of
+ {_,Opts} ->
Opts;
- _ ->
+ false ->
[]
end.
-opt(Op, [{Op, Value}|_]) ->
- {ok, Value};
-opt(Op, [_|Options]) ->
- opt(Op, Options);
-opt(_, []) ->
- false.
-
-debug_options(Opts) ->
- case opt(debug, Opts) of
- {ok, Options} -> sys:debug_options(Options);
- _ -> []
+debug_options(Name, Opts) ->
+ case lists:keyfind(debug, 1, Opts) of
+ {_,Options} ->
+ try sys:debug_options(Options)
+ catch _:_ ->
+ error_logger:format(
+ "~p: ignoring erroneous debug options - ~p~n",
+ [Name,Options]),
+ []
+ end;
+ false ->
+ []
end.
format_status_header(TagLine, Pid) when is_pid(Pid) ->
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 1303036168..ccacf658e9 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -147,16 +147,11 @@ init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
init_it(Starter, Parent, Name0, _, _, Options) ->
process_flag(trap_exit, true),
- Debug = gen:debug_options(Options),
+ Name = gen:name(Name0),
+ Debug = gen:debug_options(Name, Options),
proc_lib:init_ack(Starter, {ok, self()}),
- Name = name(Name0),
loop(Parent, Name, [], Debug, false).
-name({local,Name}) -> Name;
-name({global,Name}) -> Name;
-name({via,_, Name}) -> Name;
-name(Pid) when is_pid(Pid) -> Pid.
-
-spec add_handler(emgr_ref(), handler(), term()) -> term().
add_handler(M, Handler, Args) -> rpc(M, {add_handler, Handler, Args}).
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 2b4b76105e..6e7528fd98 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -305,64 +305,11 @@ enter_loop(Mod, Options, StateName, StateData, Timeout) ->
enter_loop(Mod, Options, StateName, StateData, self(), Timeout).
enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) ->
- Name = get_proc_name(ServerName),
- Parent = get_parent(),
- Debug = gen:debug_options(Options),
+ Name = gen:get_proc_name(ServerName),
+ Parent = gen:get_parent(),
+ Debug = gen:debug_options(Name, Options),
loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug).
-get_proc_name(Pid) when is_pid(Pid) ->
- Pid;
-get_proc_name({local, Name}) ->
- case process_info(self(), registered_name) of
- {registered_name, Name} ->
- Name;
- {registered_name, _Name} ->
- exit(process_not_registered);
- [] ->
- exit(process_not_registered)
- end;
-get_proc_name({global, Name}) ->
- case global:whereis_name(Name) of
- undefined ->
- exit(process_not_registered_globally);
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit(process_not_registered_globally)
- end;
-get_proc_name({via, Mod, Name}) ->
- case Mod:whereis_name(Name) of
- undefined ->
- exit({process_not_registered_via, Mod});
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit({process_not_registered_via, Mod})
- end.
-
-get_parent() ->
- case get('$ancestors') of
- [Parent | _] when is_pid(Parent) ->
- Parent;
- [Parent | _] when is_atom(Parent) ->
- name_to_pid(Parent);
- _ ->
- exit(process_was_not_started_by_proc_lib)
- end.
-
-name_to_pid(Name) ->
- case whereis(Name) of
- undefined ->
- case global:whereis_name(Name) of
- undefined ->
- exit(could_not_find_registered_name);
- Pid ->
- Pid
- end;
- Pid ->
- Pid
- end.
-
%%% ---------------------------------------------------
%%% Initiate the new process.
%%% Register the name using the Rfunc function
@@ -373,8 +320,8 @@ name_to_pid(Name) ->
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
- Name = name(Name0),
- Debug = gen:debug_options(Options),
+ Name = gen:name(Name0),
+ Debug = gen:debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, StateName, StateData} ->
proc_lib:init_ack(Starter, {ok, self()}),
@@ -383,15 +330,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug);
{stop, Reason} ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Reason} ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
Else ->
@@ -400,20 +347,6 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
exit(Error)
end.
-name({local,Name}) -> Name;
-name({global,Name}) -> Name;
-name({via,_, Name}) -> Name;
-name(Pid) when is_pid(Pid) -> Pid.
-
-unregister_name({local,Name}) ->
- _ = (catch unregister(Name));
-unregister_name({global,Name}) ->
- _ = global:unregister_name(Name);
-unregister_name({via, Mod, Name}) ->
- _ = Mod:unregister_name(Name);
-unregister_name(Pid) when is_pid(Pid) ->
- Pid.
-
%%-----------------------------------------------------------------
%% The MAIN loop
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index fa6fff6dca..5800aca66f 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -304,9 +304,9 @@ enter_loop(Mod, Options, State, Timeout) ->
enter_loop(Mod, Options, State, self(), Timeout).
enter_loop(Mod, Options, State, ServerName, Timeout) ->
- Name = get_proc_name(ServerName),
- Parent = get_parent(),
- Debug = debug_options(Name, Options),
+ Name = gen:get_proc_name(ServerName),
+ Parent = gen:get_parent(),
+ Debug = gen:debug_options(Name, Options),
loop(Parent, Name, State, Mod, Timeout, Debug).
%%%========================================================================
@@ -323,8 +323,8 @@ enter_loop(Mod, Options, State, ServerName, Timeout) ->
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
- Name = name(Name0),
- Debug = debug_options(Name, Options),
+ Name = gen:name(Name0),
+ Debug = gen:debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
@@ -339,15 +339,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
%% (Otherwise, the parent process could get
%% an 'already_started' error if it immediately
%% tried starting the process again.)
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Reason} ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
Else ->
@@ -356,20 +356,6 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
exit(Error)
end.
-name({local,Name}) -> Name;
-name({global,Name}) -> Name;
-name({via,_, Name}) -> Name;
-name(Pid) when is_pid(Pid) -> Pid.
-
-unregister_name({local,Name}) ->
- _ = (catch unregister(Name));
-unregister_name({global,Name}) ->
- _ = global:unregister_name(Name);
-unregister_name({via, Mod, Name}) ->
- _ = Mod:unregister_name(Name);
-unregister_name(Pid) when is_pid(Pid) ->
- Pid.
-
%%%========================================================================
%%% Internal functions
%%%========================================================================
@@ -858,86 +844,6 @@ error_info(Reason, Name, Msg, State, Debug) ->
sys:print_log(Debug),
ok.
-%%% ---------------------------------------------------
-%%% Misc. functions.
-%%% ---------------------------------------------------
-
-opt(Op, [{Op, Value}|_]) ->
- {ok, Value};
-opt(Op, [_|Options]) ->
- opt(Op, Options);
-opt(_, []) ->
- false.
-
-debug_options(Name, Opts) ->
- case opt(debug, Opts) of
- {ok, Options} -> dbg_opts(Name, Options);
- _ -> []
- end.
-
-dbg_opts(Name, Opts) ->
- case catch sys:debug_options(Opts) of
- {'EXIT',_} ->
- format("~p: ignoring erroneous debug options - ~p~n",
- [Name, Opts]),
- [];
- Dbg ->
- Dbg
- end.
-
-get_proc_name(Pid) when is_pid(Pid) ->
- Pid;
-get_proc_name({local, Name}) ->
- case process_info(self(), registered_name) of
- {registered_name, Name} ->
- Name;
- {registered_name, _Name} ->
- exit(process_not_registered);
- [] ->
- exit(process_not_registered)
- end;
-get_proc_name({global, Name}) ->
- case global:whereis_name(Name) of
- undefined ->
- exit(process_not_registered_globally);
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit(process_not_registered_globally)
- end;
-get_proc_name({via, Mod, Name}) ->
- case Mod:whereis_name(Name) of
- undefined ->
- exit({process_not_registered_via, Mod});
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit({process_not_registered_via, Mod})
- end.
-
-get_parent() ->
- case get('$ancestors') of
- [Parent | _] when is_pid(Parent)->
- Parent;
- [Parent | _] when is_atom(Parent)->
- name_to_pid(Parent);
- _ ->
- exit(process_was_not_started_by_proc_lib)
- end.
-
-name_to_pid(Name) ->
- case whereis(Name) of
- undefined ->
- case global:whereis_name(Name) of
- undefined ->
- exit(could_not_find_registered_name);
- Pid ->
- Pid
- end;
- Pid ->
- Pid
- end.
-
%%-----------------------------------------------------------------
%% Status information
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
new file mode 100644
index 0000000000..23bddafeed
--- /dev/null
+++ b/lib/stdlib/src/gen_statem.erl
@@ -0,0 +1,1308 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(gen_statem).
+
+%% API
+-export(
+ [start/3,start/4,start_link/3,start_link/4,
+ stop/1,stop/3,
+ cast/2,call/2,call/3,
+ enter_loop/5,enter_loop/6,enter_loop/7,
+ reply/1,reply/2]).
+
+%% gen callbacks
+-export(
+ [init_it/6]).
+
+%% sys callbacks
+-export(
+ [system_continue/3,
+ system_terminate/4,
+ system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
+ format_status/2]).
+
+%% Internal callbacks
+-export(
+ [wakeup_from_hibernate/3]).
+
+%% Type exports for templates
+-export_type(
+ [event_type/0,
+ callback_mode/0,
+ state_function_result/0,
+ handle_event_result/0,
+ action/0]).
+
+%% Fix problem for doc build
+-export_type([transition_option/0]).
+
+%%%==========================================================================
+%%% Interface functions.
+%%%==========================================================================
+
+-type from() ::
+ {To :: pid(), Tag :: term()}. % Reply-to specifier for call
+
+-type state() ::
+ state_name() | % For state callback function StateName/5
+ term(). % For state callback function handle_event/5
+
+-type state_name() :: atom().
+
+-type data() :: term().
+
+-type event_type() ::
+ {'call',From :: from()} | 'cast' |
+ 'info' | 'timeout' | 'internal'.
+
+-type callback_mode() :: 'state_functions' | 'handle_event_function'.
+
+-type transition_option() ::
+ postpone() | hibernate() | event_timeout().
+-type postpone() ::
+ %% If 'true' postpone the current event
+ %% and retry it when the state changes (=/=)
+ boolean().
+-type hibernate() ::
+ %% If 'true' hibernate the server instead of going into receive
+ boolean().
+-type event_timeout() ::
+ %% Generate a ('timeout', EventContent, ...) event after Time
+ %% unless some other event is delivered
+ Time :: timeout().
+
+-type action() ::
+ %% During a state change:
+ %% * NextState and NewData are set.
+ %% * All action()s are executed in order of apperance.
+ %% * Postponing the current event is performed
+ %% iff 'postpone' is 'true'.
+ %% * A state timer is started iff 'timeout' is set.
+ %% * Pending events are handled or if there are
+ %% no pending events the server goes into receive
+ %% or hibernate (iff 'hibernate' is 'true')
+ %%
+ %% These action()s are executed in order of appearence
+ %% in the containing list. The ones that set options
+ %% will override any previous so the last of each kind wins.
+ %%
+ 'postpone' | % Set the postpone option
+ {'postpone', Postpone :: postpone()} |
+ %%
+ 'hibernate' | % Set the hibernate option
+ {'hibernate', Hibernate :: hibernate()} |
+ %%
+ (Timeout :: event_timeout()) | % {timeout,Timeout}
+ {'timeout', % Set the event timeout option
+ Time :: event_timeout(), EventContent :: term()} |
+ %%
+ reply_action() |
+ %%
+ %% All 'next_event' events are kept in a list and then
+ %% inserted at state changes so the first in the
+ %% action() list is the first to be delivered.
+ {'next_event', % Insert event as the next to handle
+ EventType :: event_type(),
+ EventContent :: term()}.
+-type reply_action() ::
+ {'reply', % Reply to a caller
+ From :: from(), Reply :: term()}.
+
+-type state_function_result() ::
+ {'next_state', % {next_state,NextStateName,NewData,[]}
+ NextStateName :: state_name(),
+ NewData :: data()} |
+ {'next_state', % State transition, maybe to the same state
+ NextStateName :: state_name(),
+ NewData :: data(),
+ Actions :: [action()] | action()} |
+ common_state_callback_result().
+-type handle_event_result() ::
+ {'next_state', % {next_state,NextState,NewData,[]}
+ NextState :: state(),
+ NewData :: data()} |
+ {'next_state', % State transition, maybe to the same state
+ NextState :: state(),
+ NewData :: data(),
+ Actions :: [action()] | action()} |
+ common_state_callback_result().
+-type common_state_callback_result() ::
+ 'stop' | % {stop,normal}
+ {'stop', % Stop the server
+ Reason :: term()} |
+ {'stop', % Stop the server
+ Reason :: term(),
+ NewData :: data()} |
+ {'stop_and_reply', % Reply then stop the server
+ Reason :: term(),
+ Replies :: [reply_action()] | reply_action()} |
+ {'stop_and_reply', % Reply then stop the server
+ Reason :: term(),
+ Replies :: [reply_action()] | reply_action(),
+ NewData :: data()} |
+ {'keep_state', % {keep_state,NewData,[]}
+ NewData :: data()} |
+ {'keep_state', % Keep state, change data
+ NewData :: data(),
+ Actions :: [action()] | action()} |
+ 'keep_state_and_data' | % {keep_state_and_data,[]}
+ {'keep_state_and_data', % Keep state and data -> only actions
+ Actions :: [action()] | action()}.
+
+
+%% The state machine init function. It is called only once and
+%% the server is not running until this function has returned
+%% an {ok, ...} tuple. Thereafter the state callbacks are called
+%% for all events to this server.
+-callback init(Args :: term()) ->
+ {callback_mode(), state(), data()} |
+ {callback_mode(), state(), data(), [action()] | action()} |
+ 'ignore' |
+ {'stop', Reason :: term()}.
+
+%% Example state callback for callback_mode() =:= state_functions
+%% state name 'state_name'.
+%%
+%% In this mode all states has to be type state_name() i.e atom().
+%%
+%% Note that state callbacks and only state callbacks have arity 5
+%% and that is intended.
+-callback state_name(
+ event_type(),
+ EventContent :: term(),
+ Data :: data()) ->
+ state_function_result().
+%%
+%% State callback for callback_mode() =:= handle_event_function.
+%%
+%% Note that state callbacks and only state callbacks have arity 5
+%% and that is intended.
+-callback handle_event(
+ event_type(),
+ EventContent :: term(),
+ State :: state(), % Current state
+ Data :: data()) ->
+ handle_event_result().
+
+%% Clean up before the server terminates.
+-callback terminate(
+ Reason :: 'normal' | 'shutdown' | {'shutdown', term()}
+ | term(),
+ State :: state(),
+ Data :: data()) ->
+ any().
+
+%% Note that the new code can expect to get an OldState from
+%% the old code version not only in code_change/4 but in the first
+%% state callback function called thereafter
+-callback code_change(
+ OldVsn :: term() | {'down', term()},
+ OldState :: state(),
+ OldData :: data(),
+ Extra :: term()) ->
+ {NewCallbackMode :: callback_mode(),
+ NewState :: state(),
+ NewData :: data()}.
+
+%% Format the callback module state in some sensible that is
+%% often condensed way. For StatusOption =:= 'normal' the perferred
+%% return term is [{data,[{"State",FormattedState}]}], and for
+%% StatusOption =:= 'terminate' it is just FormattedState.
+-callback format_status(
+ StatusOption,
+ [ [{Key :: term(), Value :: term()}] |
+ state() |
+ data()]) ->
+ Status :: term() when
+ StatusOption :: 'normal' | 'terminate'.
+
+-optional_callbacks(
+ [init/1, % One may use enter_loop/5,6,7 instead
+ format_status/2, % Has got a default implementation
+ %%
+ state_name/3, % Example for callback_mode =:= state_functions:
+ %% there has to be a StateName/5 callback function for every StateName.
+ %%
+ handle_event/4]). % For callback_mode =:= handle_event_function
+
+%% Type validation functions
+callback_mode(CallbackMode) ->
+ case CallbackMode of
+ state_functions ->
+ true;
+ handle_event_function ->
+ true;
+ _ ->
+ false
+ end.
+%%
+from({Pid,_}) when is_pid(Pid) ->
+ true;
+from(_) ->
+ false.
+%%
+event_type({call,From}) ->
+ from(From);
+event_type(Type) ->
+ case Type of
+ cast ->
+ true;
+ info ->
+ true;
+ timeout ->
+ true;
+ internal ->
+ true;
+ _ ->
+ false
+ end.
+
+
+
+-define(
+ STACKTRACE(),
+ try throw(ok) catch _ -> erlang:get_stacktrace() end).
+
+%%%==========================================================================
+%%% API
+
+-type server_name() ::
+ {'global', GlobalName :: term()}
+ | {'via', RegMod :: module(), Name :: term()}
+ | {'local', atom()}.
+-type server_ref() ::
+ pid()
+ | (LocalName :: atom())
+ | {Name :: atom(), Node :: atom()}
+ | {'global', GlobalName :: term()}
+ | {'via', RegMod :: module(), ViaName :: term()}.
+-type debug_opt() ::
+ {'debug',
+ Dbgs ::
+ ['trace' | 'log' | 'statistics' | 'debug'
+ | {'logfile', string()}]}.
+-type start_opt() ::
+ debug_opt()
+ | {'timeout', Time :: timeout()}
+ | {'spawn_opt', [proc_lib:spawn_option()]}.
+-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
+
+
+
+%% Start a state machine
+-spec start(
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start(Module, Args, Opts) ->
+ gen:start(?MODULE, nolink, Module, Args, Opts).
+%%
+-spec start(
+ ServerName :: server_name(),
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start(ServerName, Module, Args, Opts) ->
+ gen:start(?MODULE, nolink, ServerName, Module, Args, Opts).
+
+%% Start and link to a state machine
+-spec start_link(
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start_link(Module, Args, Opts) ->
+ gen:start(?MODULE, link, Module, Args, Opts).
+%%
+-spec start_link(
+ ServerName :: server_name(),
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start_link(ServerName, Module, Args, Opts) ->
+ gen:start(?MODULE, link, ServerName, Module, Args, Opts).
+
+%% Stop a state machine
+-spec stop(ServerRef :: server_ref()) -> ok.
+stop(ServerRef) ->
+ gen:stop(ServerRef).
+%%
+-spec stop(
+ ServerRef :: server_ref(),
+ Reason :: term(),
+ Timeout :: timeout()) -> ok.
+stop(ServerRef, Reason, Timeout) ->
+ gen:stop(ServerRef, Reason, Timeout).
+
+%% Send an event to a state machine that arrives with type 'event'
+-spec cast(ServerRef :: server_ref(), Msg :: term()) -> ok.
+cast({global,Name}, Msg) ->
+ try global:send(Name, wrap_cast(Msg)) of
+ _ -> ok
+ catch
+ _:_ -> ok
+ end;
+cast({via,RegMod,Name}, Msg) ->
+ try RegMod:send(Name, wrap_cast(Msg)) of
+ _ -> ok
+ catch
+ _:_ -> ok
+ end;
+cast({Name,Node} = ServerRef, Msg) when is_atom(Name), is_atom(Node) ->
+ send(ServerRef, wrap_cast(Msg));
+cast(ServerRef, Msg) when is_atom(ServerRef) ->
+ send(ServerRef, wrap_cast(Msg));
+cast(ServerRef, Msg) when is_pid(ServerRef) ->
+ send(ServerRef, wrap_cast(Msg)).
+
+%% Call a state machine (synchronous; a reply is expected) that
+%% arrives with type {call,From}
+-spec call(ServerRef :: server_ref(), Request :: term()) -> Reply :: term().
+call(ServerRef, Request) ->
+ call(ServerRef, Request, infinity).
+%%
+-spec call(
+ ServerRef :: server_ref(),
+ Request :: term(),
+ Timeout :: timeout()) ->
+ Reply :: term().
+call(ServerRef, Request, infinity) ->
+ try gen:call(ServerRef, '$gen_call', Request, infinity) of
+ {ok,Reply} ->
+ Reply
+ catch
+ Class:Reason ->
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,infinity]}},
+ erlang:get_stacktrace())
+ end;
+call(ServerRef, Request, Timeout) ->
+ %% Call server through proxy process to dodge any late reply
+ Ref = make_ref(),
+ Self = self(),
+ Pid = spawn(
+ fun () ->
+ Self !
+ try gen:call(
+ ServerRef, '$gen_call', Request, Timeout) of
+ Result ->
+ {Ref,Result}
+ catch Class:Reason ->
+ {Ref,Class,Reason,erlang:get_stacktrace()}
+ end
+ end),
+ Mref = monitor(process, Pid),
+ receive
+ {Ref,Result} ->
+ demonitor(Mref, [flush]),
+ case Result of
+ {ok,Reply} ->
+ Reply
+ end;
+ {Ref,Class,Reason,Stacktrace} ->
+ demonitor(Mref, [flush]),
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
+ Stacktrace);
+ {'DOWN',Mref,_,_,Reason} ->
+ %% There is a theoretical possibility that the
+ %% proxy process gets killed between try--of and !
+ %% so this clause is in case of that
+ exit(Reason)
+ end.
+
+%% Reply from a state machine callback to whom awaits in call/2
+-spec reply([reply_action()] | reply_action()) -> ok.
+reply({reply,From,Reply}) ->
+ reply(From, Reply);
+reply(Replies) when is_list(Replies) ->
+ replies(Replies).
+%%
+-spec reply(From :: from(), Reply :: term()) -> ok.
+reply({To,Tag}, Reply) when is_pid(To) ->
+ Msg = {Tag,Reply},
+ try To ! Msg of
+ _ ->
+ ok
+ catch
+ _:_ -> ok
+ end.
+
+%% Instead of starting the state machine through start/3,4
+%% or start_link/3,4 turn the current process presumably
+%% started by proc_lib into a state machine using
+%% the same arguments as you would have returned from init/1
+-spec enter_loop(
+ Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
+ State :: state(), Data :: data()) ->
+ no_return().
+enter_loop(Module, Opts, CallbackMode, State, Data) ->
+ enter_loop(Module, Opts, CallbackMode, State, Data, self()).
+%%
+-spec enter_loop(
+ Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
+ State :: state(), Data :: data(),
+ Server_or_Actions ::
+ server_name() | pid() | [action()]) ->
+ no_return().
+enter_loop(Module, Opts, CallbackMode, State, Data, Server_or_Actions) ->
+ if
+ is_list(Server_or_Actions) ->
+ enter_loop(
+ Module, Opts, CallbackMode, State, Data,
+ self(), Server_or_Actions);
+ true ->
+ enter_loop(
+ Module, Opts, CallbackMode, State, Data,
+ Server_or_Actions, [])
+ end.
+%%
+-spec enter_loop(
+ Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
+ State :: state(), Data :: data(),
+ Server :: server_name() | pid(),
+ Actions :: [action()] | action()) ->
+ no_return().
+enter_loop(Module, Opts, CallbackMode, State, Data, Server, Actions) ->
+ is_atom(Module) orelse error({atom,Module}),
+ callback_mode(CallbackMode) orelse error({callback_mode,CallbackMode}),
+ Parent = gen:get_parent(),
+ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent).
+
+%%---------------------------------------------------------------------------
+%% API helpers
+
+wrap_cast(Event) ->
+ {'$gen_cast',Event}.
+
+replies([{reply,From,Reply}|Replies]) ->
+ reply(From, Reply),
+ replies(Replies);
+replies([]) ->
+ ok.
+
+%% Might actually not send the message in case of caught exception
+send(Proc, Msg) ->
+ try erlang:send(Proc, Msg, [noconnect]) of
+ noconnect ->
+ _ = spawn(erlang, send, [Proc,Msg]),
+ ok;
+ ok ->
+ ok
+ catch
+ _:_ ->
+ ok
+ end.
+
+%% Here the init_it/6 and enter_loop/5,6,7 functions converge
+enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
+ %% The values should already have been type checked
+ Name = gen:get_proc_name(Server),
+ Debug = gen:debug_options(Name, Opts),
+ P = Events = [],
+ Event = {internal,initial_state},
+ %% We enforce {postpone,false} to ensure that
+ %% our fake Event gets discarded, thought it might get logged
+ NewActions =
+ if
+ is_list(Actions) ->
+ Actions ++ [{postpone,false}];
+ true ->
+ [Actions,{postpone,false}]
+ end,
+ S = #{
+ callback_mode => CallbackMode,
+ module => Module,
+ name => Name,
+ %% All fields below will be replaced according to the arguments to
+ %% loop_event_actions/10 when it finally loops back to loop/3
+ state => State,
+ data => Data,
+ postponed => P,
+ hibernate => false,
+ timer => undefined},
+ NewDebug = sys_debug(Debug, S, State, {enter,Event,State}),
+ loop_event_actions(
+ Parent, NewDebug, S, Events,
+ State, Data, P, Event, State, NewActions).
+
+%%%==========================================================================
+%%% gen callbacks
+
+init_it(Starter, self, ServerRef, Module, Args, Opts) ->
+ init_it(Starter, self(), ServerRef, Module, Args, Opts);
+init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
+ try Module:init(Args) of
+ Result ->
+ init_result(Starter, Parent, ServerRef, Module, Result, Opts)
+ catch
+ Result ->
+ init_result(Starter, Parent, ServerRef, Module, Result, Opts);
+ Class:Reason ->
+ gen:unregister_name(ServerRef),
+ proc_lib:init_ack(Starter, {error,Reason}),
+ erlang:raise(Class, Reason, erlang:get_stacktrace())
+ end.
+
+%%---------------------------------------------------------------------------
+%% gen callbacks helpers
+
+init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
+ case Result of
+ {CallbackMode,State,Data} ->
+ case callback_mode(CallbackMode) of
+ true ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(
+ Module, Opts, CallbackMode, State, Data,
+ ServerRef, [], Parent);
+ false ->
+ Error = {callback_mode,CallbackMode},
+ proc_lib:init_ack(Starter, {error,Error}),
+ exit(Error)
+ end;
+ {CallbackMode,State,Data,Actions} ->
+ case callback_mode(CallbackMode) of
+ true ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(
+ Module, Opts, CallbackMode, State, Data,
+ ServerRef, Actions, Parent);
+ false ->
+ Error = {callback_mode,CallbackMode},
+ proc_lib:init_ack(Starter, {error,Error}),
+ exit(Error)
+ end;
+ {stop,Reason} ->
+ gen:unregister_name(ServerRef),
+ proc_lib:init_ack(Starter, {error,Reason}),
+ exit(Reason);
+ ignore ->
+ gen:unregister_name(ServerRef),
+ proc_lib:init_ack(Starter, ignore),
+ exit(normal);
+ _ ->
+ Error = {bad_return_value,Result},
+ proc_lib:init_ack(Starter, {error,Error}),
+ exit(Error)
+ end.
+
+%%%==========================================================================
+%%% sys callbacks
+
+system_continue(Parent, Debug, S) ->
+ loop(Parent, Debug, S).
+
+system_terminate(
+ Reason, _Parent, Debug,
+ #{state := State, data := Data, postponed := P} = S) ->
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [], State, Data, P).
+
+system_code_change(
+ #{module := Module,
+ state := State,
+ data := Data} = S,
+ _Mod, OldVsn, Extra) ->
+ case
+ try Module:code_change(OldVsn, State, Data, Extra)
+ catch
+ Result -> Result
+ end
+ of
+ {NewCallbackMode,NewState,NewData} ->
+ callback_mode(NewCallbackMode) orelse
+ error({callback_mode,NewCallbackMode}),
+ {ok,
+ S#{callback_mode := NewCallbackMode,
+ state := NewState,
+ data := NewData}};
+ {ok,_} = Error ->
+ error({case_clause,Error});
+ Error ->
+ Error
+ end.
+
+system_get_state(#{state := State, data := Data}) ->
+ {ok,{State,Data}}.
+
+system_replace_state(
+ StateFun,
+ #{state := State,
+ data := Data} = S) ->
+ {NewState,NewData} = Result = StateFun({State,Data}),
+ {ok,Result,S#{state := NewState, data := NewData}}.
+
+format_status(
+ Opt,
+ [PDict,SysState,Parent,Debug,
+ #{name := Name, postponed := P, state := State, data := Data} = S]) ->
+ Header = gen:format_status_header("Status for state machine", Name),
+ Log = sys:get_debug(log, Debug, []),
+ [{header,Header},
+ {data,
+ [{"Status",SysState},
+ {"Parent",Parent},
+ {"Logged Events",Log},
+ {"Postponed",P}]} |
+ case format_status(Opt, PDict, S, State, Data) of
+ L when is_list(L) -> L;
+ T -> [T]
+ end].
+
+%%---------------------------------------------------------------------------
+%% Format debug messages. Print them as the call-back module sees
+%% them, not as the real erlang messages. Use trace for that.
+%%---------------------------------------------------------------------------
+
+print_event(Dev, {in,Event}, {Name,_}) ->
+ io:format(
+ Dev, "*DBG* ~p received ~s~n",
+ [Name,event_string(Event)]);
+print_event(Dev, {out,Reply,{To,_Tag}}, {Name,_}) ->
+ io:format(
+ Dev, "*DBG* ~p sent ~p to ~p~n",
+ [Name,Reply,To]);
+print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
+ StateString =
+ case NextState of
+ State ->
+ io_lib:format("~p", [State]);
+ _ ->
+ io_lib:format("~p => ~p", [State,NextState])
+ end,
+ io:format(
+ Dev, "*DBG* ~p ~w ~s in state ~s~n",
+ [Name,Tag,event_string(Event),StateString]).
+
+event_string(Event) ->
+ case Event of
+ {{call,{Pid,_Tag}},Request} ->
+ io_lib:format("call ~p from ~w", [Request,Pid]);
+ {EventType,EventContent} ->
+ io_lib:format("~w ~p", [EventType,EventContent])
+ end.
+
+sys_debug(Debug, #{name := Name}, State, Entry) ->
+ case Debug of
+ [] ->
+ Debug;
+ _ ->
+ sys:handle_debug(
+ Debug, fun print_event/3, {Name,State}, Entry)
+ end.
+
+%%%==========================================================================
+%%% Internal callbacks
+
+wakeup_from_hibernate(Parent, Debug, S) ->
+ %% It is a new message that woke us up so we have to receive it now
+ loop_receive(Parent, Debug, S).
+
+%%%==========================================================================
+%%% State Machine engine implementation of proc_lib/gen server
+
+%% Server loop, consists of all loop* functions
+%% and detours through sys:handle_system_message/7 and proc_lib:hibernate/3
+
+%% Entry point for system_continue/3
+loop(Parent, Debug, #{hibernate := Hibernate} = S) ->
+ case Hibernate of
+ true ->
+ %% Does not return but restarts process at
+ %% wakeup_from_hibernate/3 that jumps to loop_receive/3
+ proc_lib:hibernate(
+ ?MODULE, wakeup_from_hibernate, [Parent,Debug,S]),
+ error(
+ {should_not_have_arrived_here_but_instead_in,
+ {wakeup_from_hibernate,3}});
+ false ->
+ loop_receive(Parent, Debug, S)
+ end.
+
+%% Entry point for wakeup_from_hibernate/3
+loop_receive(Parent, Debug, #{timer := Timer} = S) ->
+ receive
+ Msg ->
+ case Msg of
+ {system,Pid,Req} ->
+ #{hibernate := Hibernate} = S,
+ %% Does not return but tail recursively calls
+ %% system_continue/3 that jumps to loop/3
+ sys:handle_system_msg(
+ Req, Pid, Parent, ?MODULE, Debug, S, Hibernate);
+ {'EXIT',Parent,Reason} = EXIT ->
+ #{state := State, data := Data, postponed := P} = S,
+ %% EXIT is not a 2-tuple and therefore
+ %% not an event and has no event_type(),
+ %% but this will stand out in the crash report...
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [EXIT], State, Data, P);
+ {timeout,Timer,Content} when Timer =/= undefined ->
+ loop_receive_result(
+ Parent, Debug, S, {timeout,Content});
+ _ ->
+ %% Cancel Timer if running
+ case Timer of
+ undefined ->
+ ok;
+ _ ->
+ case erlang:cancel_timer(Timer) of
+ TimeLeft when is_integer(TimeLeft) ->
+ ok;
+ false ->
+ receive
+ {timeout,Timer,_} ->
+ ok
+ after 0 ->
+ ok
+ end
+ end
+ end,
+ Event =
+ case Msg of
+ {'$gen_call',From,Request} ->
+ {{call,From},Request};
+ {'$gen_cast',E} ->
+ {cast,E};
+ _ ->
+ {info,Msg}
+ end,
+ loop_receive_result(Parent, Debug, S, Event)
+ end
+ end.
+
+loop_receive_result(
+ Parent, Debug,
+ #{state := State,
+ data := Data,
+ postponed := P} = S,
+ Event) ->
+ %% The engine state map S is now dismantled
+ %% and will not be restored until we return to loop/3.
+ %%
+ %% The fields 'callback_mode', 'module', and 'name' are still valid.
+ %% The fields 'state', 'data', and 'postponed' are held in arguments.
+ %% The fields 'timer' and 'hibernate' will be recalculated.
+ %%
+ NewDebug = sys_debug(Debug, S, State, {in,Event}),
+ %% Here the queue of not yet handled events is created
+ Events = [],
+ Hibernate = false,
+ loop_event(
+ Parent, NewDebug, S, Events, State, Data, P, Event, Hibernate).
+
+%% Process the event queue, or if it is empty
+%% loop back to loop/3 to receive a new event
+loop_events(
+ Parent, Debug, S, [Event|Events],
+ State, Data, P, Hibernate, _Timeout) ->
+ %%
+ %% If there was a state timer requested we just ignore that
+ %% since we have events to handle which cancels the timer
+ loop_event(
+ Parent, Debug, S, Events, State, Data, P, Event, Hibernate);
+loop_events(
+ Parent, Debug, S, [],
+ State, Data, P, Hibernate, Timeout) ->
+ case Timeout of
+ {timeout,0,EventContent} ->
+ %% Immediate timeout - simulate it
+ %% so we do not get the timeout message
+ %% after any received event
+ loop_event(
+ Parent, Debug, S, [],
+ State, Data, P, {timeout,EventContent}, Hibernate);
+ {timeout,Time,EventContent} ->
+ %% Actually start a timer
+ Timer = erlang:start_timer(Time, self(), EventContent),
+ loop_events_done(
+ Parent, Debug, S, Timer, State, Data, P, Hibernate);
+ undefined ->
+ %% No state timeout has been requested
+ Timer = undefined,
+ loop_events_done(
+ Parent, Debug, S, Timer, State, Data, P, Hibernate)
+ end.
+%%
+loop_events_done(Parent, Debug, S, Timer, State, Data, P, Hibernate) ->
+ NewS =
+ S#{
+ state := State,
+ data := Data,
+ postponed := P,
+ hibernate := Hibernate,
+ timer := Timer},
+ loop(Parent, Debug, NewS).
+
+loop_event(
+ Parent, Debug,
+ #{callback_mode := CallbackMode,
+ module := Module} = S,
+ Events,
+ State, Data, P, {Type,Content} = Event, Hibernate) ->
+ %%
+ %% If Hibernate is true here it can only be
+ %% because it was set from an event action
+ %% and we did not go into hibernation since there
+ %% were events in queue, so we do what the user
+ %% might depend on i.e collect garbage which
+ %% would have happened if we actually hibernated
+ %% and immediately was awakened
+ Hibernate andalso garbage_collect(),
+ %%
+ try
+ case CallbackMode of
+ state_functions ->
+ Module:State(Type, Content, Data);
+ handle_event_function ->
+ Module:handle_event(Type, Content, State, Data)
+ end of
+ Result ->
+ loop_event_result(
+ Parent, Debug, S, Events, State, Data, P, Event, Result)
+ catch
+ Result ->
+ loop_event_result(
+ Parent, Debug, S, Events, State, Data, P, Event, Result);
+ error:badarg when CallbackMode =:= state_functions ->
+ case erlang:get_stacktrace() of
+ [{erlang,apply,[Module,State,_],_}|Stacktrace] ->
+ Args = [Type,Content,Data],
+ terminate(
+ error,
+ {undef_state_function,{Module,State,Args}},
+ Stacktrace,
+ Debug, S, [Event|Events], State, Data, P);
+ Stacktrace ->
+ terminate(
+ error, badarg, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
+ end;
+ error:undef ->
+ %% Process an undef to check for the simple mistake
+ %% of calling a nonexistent state function
+ case erlang:get_stacktrace() of
+ [{Module,State,
+ [Type,Content,Data]=Args,
+ _}
+ |Stacktrace]
+ when CallbackMode =:= state_functions ->
+ terminate(
+ error,
+ {undef_state_function,{Module,State,Args}},
+ Stacktrace,
+ Debug, S, [Event|Events], State, Data, P);
+ [{Module,handle_event,
+ [Type,Content,State,Data]=Args,
+ _}
+ |Stacktrace]
+ when CallbackMode =:= handle_event_function ->
+ terminate(
+ error,
+ {undef_state_function,{Module,handle_event,Args}},
+ Stacktrace,
+ Debug, S, [Event|Events], State, Data, P);
+ Stacktrace ->
+ terminate(
+ error, undef, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
+ end;
+ Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
+ end.
+
+%% Interpret all callback return variants
+loop_event_result(
+ Parent, Debug, S, Events, State, Data, P, Event, Result) ->
+ case Result of
+ stop ->
+ terminate(
+ exit, normal, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P);
+ {stop,Reason} ->
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P);
+ {stop,Reason,NewData} ->
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ {stop_and_reply,Reason,Replies} ->
+ Q = [Event|Events],
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, Q, State, Data, P, Replies);
+ {stop_and_reply,Reason,Replies,NewData} ->
+ Q = [Event|Events],
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, Q, State, NewData, P, Replies);
+ {next_state,NextState,NewData} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, []);
+ {next_state,NextState,NewData,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions);
+ {keep_state,NewData} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, State, []);
+ {keep_state,NewData,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, State, Actions);
+ keep_state_and_data ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, Data, P, Event, State, []);
+ {keep_state_and_data,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, Data, P, Event, State, Actions);
+ _ ->
+ terminate(
+ error, {bad_return_value,Result}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P)
+ end.
+
+loop_event_actions(
+ Parent, Debug, S, Events, State, NewData, P, Event, NextState, Actions) ->
+ Postpone = false, % Shall we postpone this event; boolean()
+ Hibernate = false,
+ Timeout = undefined,
+ NextEvents = [],
+ loop_event_actions(
+ Parent, Debug, S, Events, State, NewData, P, Event, NextState,
+ if
+ is_list(Actions) ->
+ Actions;
+ true ->
+ [Actions]
+ end,
+ Postpone, Hibernate, Timeout, NextEvents).
+%%
+%% Process all actions
+loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, [Action|Actions],
+ Postpone, Hibernate, Timeout, NextEvents) ->
+ case Action of
+ %% Actual actions
+ {reply,From,Reply} ->
+ case from(From) of
+ true ->
+ NewDebug = do_reply(Debug, S, State, From, Reply),
+ loop_event_actions(
+ Parent, NewDebug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, Timeout, NextEvents);
+ false ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
+ end;
+ {next_event,Type,Content} ->
+ case event_type(Type) of
+ true ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, Timeout,
+ [{Type,Content}|NextEvents]);
+ false ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
+ end;
+ %% Actions that set options
+ {postpone,NewPostpone} when is_boolean(NewPostpone) ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ NewPostpone, Hibernate, Timeout, NextEvents);
+ {postpone,_} ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ postpone ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ true, Hibernate, Timeout, NextEvents);
+ {hibernate,NewHibernate} when is_boolean(NewHibernate) ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, NewHibernate, Timeout, NextEvents);
+ {hibernate,_} ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ hibernate ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, true, Timeout, NextEvents);
+ {timeout,infinity,_} -> % Clear timer - it will never trigger
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, undefined, NextEvents);
+ {timeout,Time,_} = NewTimeout when is_integer(Time), Time >= 0 ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, NewTimeout, NextEvents);
+ {timeout,_,_} ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ infinity -> % Clear timer - it will never trigger
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, undefined, NextEvents);
+ Time when is_integer(Time), Time >= 0 ->
+ NewTimeout = {timeout,Time,Time},
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, NewTimeout, NextEvents);
+ _ ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
+ end;
+%%
+%% End of actions list
+loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P0, Event, NextState, [],
+ Postpone, Hibernate, Timeout, NextEvents) ->
+ %%
+ %% All options have been collected and next_events are buffered.
+ %% Do the actual state transition.
+ %%
+ P1 = % Move current event to postponed if Postpone
+ case Postpone of
+ true ->
+ [Event|P0];
+ false ->
+ P0
+ end,
+ {Q2,P} = % Move all postponed events to queue if state change
+ if
+ NextState =:= State ->
+ {Events,P1};
+ true ->
+ {lists:reverse(P1, Events),[]}
+ end,
+ %% Place next events first in queue
+ Q = lists:reverse(NextEvents, Q2),
+ %%
+ NewDebug =
+ sys_debug(
+ Debug, S, State,
+ case Postpone of
+ true ->
+ {postpone,Event,NextState};
+ false ->
+ {consume,Event,NextState}
+ end),
+ loop_events(
+ Parent, NewDebug, S, Q, NextState, NewData, P, Hibernate, Timeout).
+
+%%---------------------------------------------------------------------------
+%% Server helpers
+
+reply_then_terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, Replies) ->
+ if
+ is_list(Replies) ->
+ do_reply_then_terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, Replies);
+ true ->
+ do_reply_then_terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, [Replies])
+ end.
+%%
+do_reply_then_terminate(
+ Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, []) ->
+ terminate(Class, Reason, Stacktrace, Debug, S, Q, State, Data, P);
+do_reply_then_terminate(
+ Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, [R|Rs]) ->
+ case R of
+ {reply,{_To,_Tag}=From,Reply} ->
+ NewDebug = do_reply(Debug, S, State, From, Reply),
+ do_reply_then_terminate(
+ Class, Reason, Stacktrace,
+ NewDebug, S, Q, State, Data, P, Rs);
+ _ ->
+ terminate(
+ error, {bad_action,R}, ?STACKTRACE(),
+ Debug, S, Q, State, Data, P)
+ end.
+
+do_reply(Debug, S, State, From, Reply) ->
+ reply(From, Reply),
+ sys_debug(Debug, S, State, {out,Reply,From}).
+
+
+terminate(
+ Class, Reason, Stacktrace,
+ Debug, #{module := Module} = S, Q, State, Data, P) ->
+ try Module:terminate(Reason, State, Data) of
+ _ -> ok
+ catch
+ _ -> ok;
+ C:R ->
+ ST = erlang:get_stacktrace(),
+ error_info(
+ C, R, ST, Debug, S, Q, P,
+ format_status(terminate, get(), S, State, Data)),
+ erlang:raise(C, R, ST)
+ end,
+ case Reason of
+ normal -> ok;
+ shutdown -> ok;
+ {shutdown,_} -> ok;
+ _ ->
+ error_info(
+ Class, Reason, Stacktrace, Debug, S, Q, P,
+ format_status(terminate, get(), S, State, Data))
+ end,
+ case Stacktrace of
+ [] ->
+ erlang:Class(Reason);
+ _ ->
+ erlang:raise(Class, Reason, Stacktrace)
+ end.
+
+error_info(
+ Class, Reason, Stacktrace, Debug,
+ #{name := Name, callback_mode := CallbackMode},
+ Q, P, FmtData) ->
+ {FixedReason,FixedStacktrace} =
+ case Stacktrace of
+ [{M,F,Args,_}|ST]
+ when Class =:= error, Reason =:= undef ->
+ case code:is_loaded(M) of
+ false ->
+ {{'module could not be loaded',M},ST};
+ _ ->
+ Arity =
+ if
+ is_list(Args) ->
+ length(Args);
+ is_integer(Args) ->
+ Args
+ end,
+ case erlang:function_exported(M, F, Arity) of
+ true ->
+ {Reason,Stacktrace};
+ false ->
+ {{'function not exported',{M,F,Arity}},
+ ST}
+ end
+ end;
+ _ -> {Reason,Stacktrace}
+ end,
+ error_logger:format(
+ "** State machine ~p terminating~n" ++
+ case Q of
+ [] -> "";
+ _ -> "** Last event = ~p~n"
+ end ++
+ "** When server state = ~p~n" ++
+ "** Reason for termination = ~w:~p~n" ++
+ "** Callback mode = ~p~n" ++
+ case Q of
+ [_,_|_] -> "** Queued = ~p~n";
+ _ -> ""
+ end ++
+ case P of
+ [] -> "";
+ _ -> "** Postponed = ~p~n"
+ end ++
+ case FixedStacktrace of
+ [] -> "";
+ _ -> "** Stacktrace =~n** ~p~n"
+ end,
+ [Name |
+ case Q of
+ [] -> [];
+ [Event|_] -> [Event]
+ end] ++
+ [FmtData,Class,FixedReason,
+ CallbackMode] ++
+ case Q of
+ [_|[_|_] = Events] -> [Events];
+ _ -> []
+ end ++
+ case P of
+ [] -> [];
+ _ -> [P]
+ end ++
+ case FixedStacktrace of
+ [] -> [];
+ _ -> [FixedStacktrace]
+ end),
+ sys:print_log(Debug),
+ ok.
+
+
+%% Call Module:format_status/2 or return a default value
+format_status(Opt, PDict, #{module := Module}, State, Data) ->
+ case erlang:function_exported(Module, format_status, 2) of
+ true ->
+ try Module:format_status(Opt, [PDict,State,Data])
+ catch
+ Result -> Result;
+ _:_ ->
+ format_status_default(
+ Opt, State,
+ "Module:format_status/2 crashed")
+ end;
+ false ->
+ format_status_default(Opt, State, Data)
+ end.
+
+%% The default Module:format_status/2
+format_status_default(Opt, State, Data) ->
+ SSD = {State,Data},
+ case Opt of
+ terminate ->
+ SSD;
+ _ ->
+ [{data,[{"State",SSD}]}]
+ end.
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index f070a7192d..ad98bc0420 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -112,14 +112,14 @@
-type format_spec() ::
#{
- control_char => char(),
- args => [any()],
- width => 'none' | integer(),
- adjust => 'left' | 'right',
- precision => 'none' | integer(),
- pad_char => char(),
- encoding => 'unicode' | 'latin1',
- strings => boolean()
+ control_char := char(),
+ args := [any()],
+ width := 'none' | integer(),
+ adjust := 'left' | 'right',
+ precision := 'none' | integer(),
+ pad_char := char(),
+ encoding := 'unicode' | 'latin1',
+ strings := boolean()
}.
%%----------------------------------------------------------------------
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index 2b4472cdf7..af9d63ddd6 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -39,7 +39,8 @@
-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,
partition/2,zf/2,filtermap/2,
mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2,
- split/2]).
+ split/2,
+ join/2]).
%%% BIFs
-export([keyfind/3, keymember/3, keysearch/3, member/2, reverse/2]).
@@ -1439,6 +1440,18 @@ split(N, [H|T], R) ->
split(_, [], _) ->
badarg.
+-spec join(Sep, List1) -> List2 when
+ Sep :: T,
+ List1 :: [T],
+ List2 :: [T],
+ T :: term().
+
+join(_Sep, []) -> [];
+join(Sep, [H|T]) -> [H|join_prepend(Sep, T)].
+
+join_prepend(_Sep, []) -> [];
+join_prepend(Sep, [H|T]) -> [Sep,H|join_prepend(Sep,T)].
+
%%% =================================================================
%%% Here follows the implementation of the sort functions.
%%%
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index a52928f77f..5dafdb282a 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -20,17 +20,18 @@
-module(maps).
--export([get/3,filter/2,fold/3, map/2,
- size/1,
+-export([get/3, filter/2,fold/3,
+ map/2, size/1,
+ update_with/3, update_with/4,
without/2, with/2]).
-
-%%% BIFs
+%% BIFs
-export([get/2, find/2, from_list/1,
is_key/2, keys/1, merge/2,
- new/0, put/3, remove/2,
+ new/0, put/3, remove/2, take/2,
to_list/1, update/3, values/1]).
+%% Shadowed by erl_bif_types: maps:get/2
-spec get(Key,Map) -> Value when
Key :: term(),
Map :: map(),
@@ -46,7 +47,7 @@ get(_,_) -> erlang:nif_error(undef).
find(_,_) -> erlang:nif_error(undef).
-
+%% Shadowed by erl_bif_types: maps:from_list/1
-spec from_list(List) -> Map when
List :: [{Key,Value}],
Key :: term(),
@@ -56,6 +57,7 @@ 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().
@@ -71,6 +73,7 @@ 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(),
@@ -86,6 +89,7 @@ merge(_,_) -> erlang:nif_error(undef).
new() -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:put/3
-spec put(Key,Value,Map1) -> Map2 when
Key :: term(),
Value :: term(),
@@ -102,7 +106,15 @@ put(_,_,_) -> erlang:nif_error(undef).
remove(_,_) -> erlang:nif_error(undef).
+-spec take(Key,Map1) -> {Value,Map2} | error when
+ Key :: term(),
+ Map1 :: map(),
+ Value :: term(),
+ Map2 :: map().
+
+take(_,_) -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:to_list/1
-spec to_list(Map) -> [{Key,Value}] when
Map :: map(),
Key :: term(),
@@ -111,6 +123,7 @@ 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(),
@@ -127,8 +140,40 @@ update(_,_,_) -> erlang:nif_error(undef).
values(_) -> erlang:nif_error(undef).
+%% End of BIFs
+
+-spec update_with(Key,Fun,Map1) -> Map2 when
+ Key :: term(),
+ Map1 :: map(),
+ Map2 :: map(),
+ Fun :: fun((Value1 :: term()) -> Value2 :: term()).
+
+update_with(Key,Fun,Map) when is_function(Fun,1), is_map(Map) ->
+ try maps:get(Key,Map) of
+ Val -> maps:update(Key,Fun(Val),Map)
+ catch
+ error:{badkey,_} ->
+ erlang:error({badkey,Key},[Key,Fun,Map])
+ end;
+update_with(Key,Fun,Map) ->
+ erlang:error(error_type(Map),[Key,Fun,Map]).
+
+
+-spec update_with(Key,Fun,Init,Map1) -> Map2 when
+ Key :: term(),
+ Map1 :: Map1,
+ Map2 :: Map2,
+ Fun :: fun((Value1 :: term()) -> Value2 :: term()),
+ Init :: term().
+
+update_with(Key,Fun,Init,Map) when is_function(Fun,1), is_map(Map) ->
+ case maps:find(Key,Map) of
+ {ok,Val} -> maps:update(Key,Fun(Val),Map);
+ error -> maps:put(Key,Init,Map)
+ end;
+update_with(Key,Fun,Init,Map) ->
+ erlang:error(error_type(Map),[Key,Fun,Init,Map]).
-%%% End of BIFs
-spec get(Key, Map, Default) -> Value | Default when
Key :: term(),
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index 052dffdbfd..7a59523f06 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -58,7 +58,12 @@ obsolete_1(erlang, now, 0) ->
obsolete_1(calendar, local_time_to_universal_time, 1) ->
{deprecated, {calendar, local_time_to_universal_time_dst, 1}};
-%% *** CRYPTO add in R16B01 ***
+%% *** CRYPTO added in OTP 19 ***
+
+obsolete_1(crypto, rand_bytes, 1) ->
+ {deprecated, {crypto, strong_rand_bytes, 1}};
+
+%% *** CRYPTO added in R16B01 ***
obsolete_1(crypto, md4, 1) ->
{deprecated, {crypto, hash, 2}};
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index c47d30c8b8..fdc2ae4070 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -48,7 +48,9 @@
| {'priority', priority_level()}
| {'min_heap_size', non_neg_integer()}
| {'min_bin_vheap_size', non_neg_integer()}
- | {'fullsweep_after', non_neg_integer()}.
+ | {'fullsweep_after', non_neg_integer()}
+ | {'message_queue_data',
+ 'off_heap' | 'on_heap' | 'mixed' }.
-type dict_or_pid() :: pid()
| (ProcInfo :: [_])
@@ -472,13 +474,15 @@ trans_init(gen,init_it,[gen_server,_,_,supervisor_bridge,[Module|_],_]) ->
{supervisor_bridge,Module,1};
trans_init(gen,init_it,[gen_server,_,_,_,supervisor_bridge,[Module|_],_]) ->
{supervisor_bridge,Module,1};
-trans_init(gen,init_it,[gen_server,_,_,Module,_,_]) ->
+trans_init(gen,init_it,[GenMod,_,_,Module,_,_])
+ when GenMod =:= gen_server;
+ GenMod =:= gen_statem;
+ GenMod =:= gen_fsm ->
{Module,init,1};
-trans_init(gen,init_it,[gen_server,_,_,_,Module|_]) ->
- {Module,init,1};
-trans_init(gen,init_it,[gen_fsm,_,_,Module,_,_]) ->
- {Module,init,1};
-trans_init(gen,init_it,[gen_fsm,_,_,_,Module|_]) ->
+trans_init(gen,init_it,[GenMod,_,_,_,Module|_])
+ when GenMod =:= gen_server;
+ GenMod =:= gen_statem;
+ GenMod =:= gen_fsm ->
{Module,init,1};
trans_init(gen,init_it,[gen_event|_]) ->
{gen_event,init_it,6};
diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
index d455abf7b0..93409d95df 100644
--- a/lib/stdlib/src/rand.erl
+++ b/lib/stdlib/src/rand.erl
@@ -44,11 +44,11 @@
%% This depends on the algorithm handler function
-type alg_seed() :: exs64_state() | exsplus_state() | exs1024_state().
%% This is the algorithm handler function within this module
--type alg_handler() :: #{type => alg(),
- max => integer(),
- next => fun(),
- uniform => fun(),
- uniform_n => fun()}.
+-type alg_handler() :: #{type := alg(),
+ max := integer(),
+ next := fun(),
+ uniform := fun(),
+ uniform_n := fun()}.
%% Internal state
-opaque state() :: {alg_handler(), alg_seed()}.
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index e3950ee795..32bcdc4f2a 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -65,6 +65,7 @@
gen_event,
gen_fsm,
gen_server,
+ gen_statem,
io,
io_lib,
io_lib_format,
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 37c3927739..38b764541a 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -57,8 +57,8 @@
| {'global', Name :: atom()}
| {'via', Module :: module(), Name :: any()}
| pid().
--type child_spec() :: #{id => child_id(), % mandatory
- start => mfargs(), % mandatory
+-type child_spec() :: #{id := child_id(), % mandatory
+ start := mfargs(), % mandatory
restart => restart(), % optional
shutdown => shutdown(), % optional
type => worker(), % optional
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 287f63b2be..28c35aed55 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -46,6 +46,7 @@ MODULES= \
gen_event_SUITE \
gen_fsm_SUITE \
gen_server_SUITE \
+ gen_statem_SUITE \
id_transform_SUITE \
io_SUITE \
io_proto_SUITE \
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index ef2c912c57..4078513e38 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -27,7 +27,8 @@
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_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1,
- otp_11728/1, encoding/1, extends/1, function_macro/1]).
+ otp_11728/1, encoding/1, extends/1, function_macro/1,
+ test_error/1, test_warning/1]).
-export([epp_parse_erl_form/2]).
@@ -67,7 +68,7 @@ all() ->
not_circular, skip_header, otp_6277, otp_7702, otp_8130,
overload_mac, otp_8388, otp_8470, otp_8562,
otp_8665, otp_8911, otp_10302, otp_10820, otp_11728,
- encoding, extends, function_macro].
+ encoding, extends, function_macro, test_error, test_warning].
groups() ->
[{upcase_mac, [], [upcase_mac_1, upcase_mac_2]},
@@ -1055,7 +1056,65 @@ ifdef(Config) ->
],
[] = run(Config, Ts).
+%% OTP-12847: Test the -error directive.
+test_error(Config) ->
+ Cs = [{error_c1,
+ <<"-error(\"string and macro: \" ?MODULE_STRING).\n"
+ "-ifdef(NOT_DEFINED).\n"
+ " -error(\"this one will be skipped\").\n"
+ "-endif.\n">>,
+ {errors,[{1,epp,{error,"string and macro: epp_test"}}],[]}},
+
+ {error_c2,
+ <<"-ifdef(CONFIG_A).\n"
+ " t() -> a.\n"
+ "-else.\n"
+ "-ifdef(CONFIG_B).\n"
+ " t() -> b.\n"
+ "-else.\n"
+ "-error(\"Neither CONFIG_A nor CONFIG_B are available\").\n"
+ "-endif.\n"
+ "-endif.\n">>,
+ {errors,[{7,epp,{error,"Neither CONFIG_A nor CONFIG_B are available"}}],[]}},
+
+ {error_c3,
+ <<"-error(a b c).\n">>,
+ {errors,[{1,epp,{bad,error}}],[]}}
+ ],
+
+ [] = compile(Config, Cs),
+ ok.
+
+%% OTP-12847: Test the -warning directive.
+test_warning(Config) ->
+ Cs = [{warn_c1,
+ <<"-warning({a,term,?MODULE}).\n"
+ "-ifdef(NOT_DEFINED).\n"
+ "-warning(\"this one will be skipped\").\n"
+ "-endif.\n">>,
+ {warnings,[{1,epp,{warning,{a,term,epp_test}}}]}},
+
+ {warn_c2,
+ <<"-ifdef(CONFIG_A).\n"
+ " t() -> a.\n"
+ "-else.\n"
+ "-ifdef(CONFIG_B).\n"
+ " t() -> b.\n"
+ "-else.\n"
+ " t() -> c.\n"
+ "-warning(\"Using fallback\").\n"
+ "-endif.\n"
+ "-endif.\n">>,
+ {warnings,[{8,epp,{warning,"Using fallback"}}]}},
+
+ {warn_c3,
+ <<"-warning(a b c).\n">>,
+ {errors,[{1,epp,{bad,warning}}],[]}}
+ ],
+
+ [] = compile(Config, Cs),
+ ok.
%% Advanced test on overloading macros.
overload_mac(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 6fea198af3..b0214e5238 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -33,47 +33,38 @@
-define(privdir, proplists:get_value(priv_dir, Conf)).
-endif.
--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([
- unused_vars_warn_basic/1,
- unused_vars_warn_lc/1,
- unused_vars_warn_rec/1,
- unused_vars_warn_fun/1,
- unused_vars_OTP_4858/1,
- unused_unsafe_vars_warn/1,
- export_vars_warn/1,
- shadow_vars/1,
- unused_import/1,
- unused_function/1,
- unsafe_vars/1,unsafe_vars2/1,
- unsafe_vars_try/1,
- unsized_binary_in_bin_gen_pattern/1,
- 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, otp_11872/1,
- export_all/1,
- bif_clash/1,
- behaviour_basic/1, behaviour_multiple/1, otp_11861/1,
- otp_7550/1,
- otp_8051/1,
- format_warn/1,
- on_load_successful/1, on_load_failing/1,
- too_many_arguments/1,
- basic_errors/1,bin_syntax_errors/1,
- predef/1,
- maps/1,maps_type/1,otp_11851/1,otp_11879/1,otp_13230/1,
- record_errors/1
- ]).
-
-init_per_testcase(_Case, Config) ->
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
+-export([all/0, suite/0, groups/0]).
+
+-export([unused_vars_warn_basic/1,
+ unused_vars_warn_lc/1,
+ unused_vars_warn_rec/1,
+ unused_vars_warn_fun/1,
+ unused_vars_OTP_4858/1,
+ unused_unsafe_vars_warn/1,
+ export_vars_warn/1,
+ shadow_vars/1,
+ unused_import/1,
+ unused_function/1,
+ unsafe_vars/1,unsafe_vars2/1,
+ unsafe_vars_try/1,
+ unsized_binary_in_bin_gen_pattern/1,
+ 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, otp_11872/1,
+ export_all/1,
+ bif_clash/1,
+ behaviour_basic/1, behaviour_multiple/1, otp_11861/1,
+ otp_7550/1,
+ otp_8051/1,
+ format_warn/1,
+ on_load_successful/1, on_load_failing/1,
+ too_many_arguments/1,
+ basic_errors/1,bin_syntax_errors/1,
+ predef/1,
+ maps/1,maps_type/1,maps_parallel_match/1,
+ otp_11851/1,otp_11879/1,otp_13230/1,
+ record_errors/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -91,7 +82,8 @@ all() ->
bif_clash, behaviour_basic, behaviour_multiple, otp_11861,
otp_7550, otp_8051, format_warn, {group, on_load},
too_many_arguments, basic_errors, bin_syntax_errors, predef,
- maps, maps_type, otp_11851, otp_11879, otp_13230,
+ maps, maps_type, maps_parallel_match,
+ otp_11851, otp_11879, otp_13230,
record_errors].
groups() ->
@@ -101,19 +93,6 @@ groups() ->
unused_vars_OTP_4858, unused_unsafe_vars_warn]},
{on_load, [], [on_load_successful, on_load_failing]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%% Warnings for unused variables in some simple cases.
unused_vars_warn_basic(Config) when is_list(Config) ->
@@ -3583,8 +3562,6 @@ predef(Config) when is_list(Config) ->
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,
@@ -3626,6 +3603,26 @@ maps(Config) ->
{errors,[{1,erl_lint,illegal_map_construction},
{1,erl_lint,{unbound_var,'X'}}],
[]}},
+ {legal_map_pattern,
+ <<"
+ -record(mapkey, {a=1,b=2}).
+ t(M,K1) ->
+ #{ a := 1,
+ $a := 1, $z := 99,
+ #{a=>val} := 2,
+ K1 := 1337,
+ #mapkey{a = 10} := wat,
+ #{{a,val}=>val} := 2,
+ #{ \"hi\" => wazzup, hi => ho } := yep,
+ ok := 1.0,
+ [3+3] := nope,
+ 1.0 := yep,
+ {3.0+3} := nope,
+ {yep} := yep
+ } = M.
+ ">>,
+ [],
+ []},
{legal_map_construction,
<<"t(V) -> #{ a => 1,
#{a=>V} => 2,
@@ -3692,6 +3689,51 @@ maps_type(Config) when is_list(Config) ->
[] = run(Config, Ts),
ok.
+maps_parallel_match(Config) when is_list(Config) ->
+ Ts = [{parallel_map_patterns_unbound1,
+ <<"
+ t(#{} = M) ->
+ #{K := V} = #{k := K} = M,
+ V.
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{unbound_var,'K'}}],[]}},
+ {parallel_map_patterns_unbound2,
+ <<"
+ t(#{} = M) ->
+ #{K1 := V1} =
+ #{K2 := V2} =
+ #{k1 := K1,k2 := K2} = M,
+ [V1,V2].
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{unbound_var,'K1'}},
+ {3,erl_lint,{unbound_var,'K1'}},
+ {4,erl_lint,{unbound_var,'K2'}},
+ {4,erl_lint,{unbound_var,'K2'}}],[]}},
+ {parallel_map_patterns_bound,
+ <<"
+ t(#{} = M,K1,K2) ->
+ #{K1 := V1} =
+ #{K2 := V2} =
+ #{k1 := K1,k2 := K2} = M,
+ [V1,V2].
+ ">>,
+ [],
+ []},
+ {parallel_map_patterns_literal,
+ <<"
+ t(#{} = M) ->
+ #{k1 := V1} =
+ #{k2 := V2} =
+ #{k1 := V1,k2 := V2} = M,
+ [V1,V2].
+ ">>,
+ [],
+ []}],
+ [] = run(Config, Ts),
+ ok.
+
%% OTP-11851: More atoms can be used as type names + bug fixes.
otp_11851(Config) when is_list(Config) ->
Ts = [
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 6dc285e448..a48ba7b5b7 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -50,7 +50,7 @@
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,
- otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1]).
+ otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1, pr_1014/1]).
%% Internal export.
-export([ehook/6]).
@@ -79,7 +79,7 @@ groups() ->
{tickets, [],
[otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
otp_8473, otp_8522, otp_8567, otp_8664, otp_9147,
- otp_10302, otp_10820, otp_11100, otp_11861]}].
+ otp_10302, otp_10820, otp_11100, otp_11861, pr_1014]}].
init_per_suite(Config) ->
Config.
@@ -902,6 +902,7 @@ maps_syntax(Config) when is_list(Config) ->
"-compile(export_all).\n"
"-type t1() :: map().\n"
"-type t2() :: #{ atom() => integer(), atom() => float() }.\n"
+ "-type t3() :: #{ atom() := integer(), atom() := float() }.\n"
"-type u() :: #{a => (I :: integer()) | (A :: atom()),\n"
" (X :: atom()) | (Y :: atom()) =>\n"
" (I :: integer()) | (A :: atom())}.\n"
@@ -1106,6 +1107,24 @@ otp_11861(Config) when is_list(Config) ->
pf(Form) ->
lists:flatten(erl_pp:form(Form, none)).
+pr_1014(Config) ->
+ ok = pp_forms(<<"-type t() :: #{_ => _}. ">>),
+ ok = pp_forms(<<"-type t() :: #{any() => _}. ">>),
+ ok = pp_forms(<<"-type t() :: #{_ => any()}. ">>),
+ ok = pp_forms(<<"-type t() :: #{any() => any()}. ">>),
+ ok = pp_forms(<<"-type t() :: #{...}. ">>),
+ ok = pp_forms(<<"-type t() :: #{atom() := integer(), ...}. ">>),
+
+ FileName = filename('pr_1014.erl', Config),
+ C = <<"-module pr_1014.\n"
+ "-compile export_all.\n"
+ "-type m() :: #{..., a := integer()}.\n">>,
+ ok = file:write_file(FileName, C),
+ {error,[{_,[{3,erl_parse,["syntax error before: ","','"]}]}],_} =
+ compile:file(FileName, [return]),
+
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->
diff --git a/lib/stdlib/test/error_logger_forwarder.erl b/lib/stdlib/test/error_logger_forwarder.erl
index 0aecd41ba9..6bbf180585 100644
--- a/lib/stdlib/test/error_logger_forwarder.erl
+++ b/lib/stdlib/test/error_logger_forwarder.erl
@@ -20,7 +20,7 @@
-module(error_logger_forwarder).
%% API.
--export([register/0]).
+-export([register/0, unregister/0]).
%% Internal export for error_logger.
-export([init/1,
@@ -33,6 +33,10 @@
register() ->
error_logger:add_report_handler(?MODULE, self()).
+unregister() ->
+ Self = self(),
+ Self = error_logger:delete_report_handler(?MODULE).
+
init(Tester) ->
{ok,Tester}.
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
new file mode 100644
index 0000000000..364314f91b
--- /dev/null
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -0,0 +1,1584 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(gen_statem_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+-behaviour(gen_statem).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
+all() ->
+ [{group, start},
+ {group, start_handle_event},
+ {group, stop},
+ {group, stop_handle_event},
+ {group, abnormal},
+ {group, abnormal_handle_event},
+ shutdown, stop_and_reply, event_order,
+ {group, sys},
+ hibernate, enter_loop].
+
+groups() ->
+ [{start, [],
+ [start1, start2, start3, start4, start5, start6, start7,
+ start8, start9, start10, start11, start12, next_events]},
+ {start_handle_event, [],
+ [start1, start2, start3, start4, start5, start6, start7,
+ start8, start9, start10, start11, start12, next_events]},
+ {stop, [],
+ [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]},
+ {stop_handle_event, [],
+ [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]},
+ {abnormal, [], [abnormal1, abnormal2]},
+ {abnormal_handle_event, [], [abnormal1, abnormal2]},
+ {sys, [],
+ [sys1, code_change,
+ call_format_status,
+ error_format_status, terminate_crash_format,
+ get_state, replace_state]},
+ {sys_handle_event, [],
+ [sys1,
+ call_format_status,
+ error_format_status, terminate_crash_format,
+ get_state, replace_state]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(GroupName, Config)
+ when GroupName =:= start_handle_event;
+ GroupName =:= stop_handle_event;
+ GroupName =:= abnormal_handle_event;
+ GroupName =:= sys_handle_event ->
+ [{callback_mode,handle_event_function}|Config];
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(_CaseName, Config) ->
+ flush(),
+%%% dbg:tracer(),
+%%% dbg:p(all, c),
+%%% dbg:tpl(gen_statem, cx),
+%%% dbg:tpl(proc_lib, cx),
+%%% dbg:tpl(gen, cx),
+%%% dbg:tpl(sys, cx),
+ Config.
+
+end_per_testcase(_CaseName, Config) ->
+%%% dbg:stop(),
+ Config.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(EXPECT_FAILURE(Code, Reason),
+ try begin Code end of
+ Reason ->
+ ct:fail({unexpected,Reason})
+ catch
+ error:Reason -> Reason;
+ exit:Reason -> Reason
+ end).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% anonymous
+start1(Config) ->
+ %%OldFl = process_flag(trap_exit, true),
+
+ {ok,Pid0} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
+ ok = do_func_test(Pid0),
+ ok = do_sync_func_test(Pid0),
+ stop_it(Pid0),
+%% stopped = gen_statem:call(Pid0, stop),
+%% timeout =
+%% ?EXPECT_FAILURE(gen_statem:call(Pid0, hej), Reason),
+
+ %%process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq().
+
+%% anonymous w. shutdown
+start2(Config) ->
+ %% Dont link when shutdown
+ {ok,Pid0} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ ok = do_func_test(Pid0),
+ ok = do_sync_func_test(Pid0),
+ stopped = gen_statem:call(Pid0, {stop,shutdown}),
+ check_stopped(Pid0),
+ ok = verify_empty_msgq().
+
+%% anonymous with timeout
+start3(Config) ->
+ %%OldFl = process_flag(trap_exit, true),
+
+ {ok,Pid0} =
+ gen_statem:start(?MODULE, start_arg(Config, []), [{timeout,5}]),
+ ok = do_func_test(Pid0),
+ ok = do_sync_func_test(Pid0),
+ stop_it(Pid0),
+
+ {error,timeout} =
+ gen_statem:start(
+ ?MODULE, start_arg(Config, sleep), [{timeout,5}]),
+
+ %%process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq().
+
+%% anonymous with ignore
+start4(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ ignore = gen_statem:start(?MODULE, start_arg(Config, ignore), []),
+
+ process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq().
+
+%% anonymous with stop
+start5(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ {error,stopped} = gen_statem:start(?MODULE, start_arg(Config, stop), []),
+
+ process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq().
+
+%% anonymous linked
+start6(Config) ->
+ {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
+ ok = do_func_test(Pid),
+ ok = do_sync_func_test(Pid),
+ stop_it(Pid),
+
+ ok = verify_empty_msgq().
+
+%% global register linked
+start7(Config) ->
+ STM = {global,my_stm},
+
+ {ok,Pid} =
+ gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid),
+ ok = do_sync_func_test(Pid),
+ ok = do_func_test(STM),
+ ok = do_sync_func_test(STM),
+ stop_it(STM),
+
+ ok = verify_empty_msgq().
+
+
+%% local register
+start8(Config) ->
+ %%OldFl = process_flag(trap_exit, true),
+ Name = my_stm,
+ STM = {local,Name},
+
+ {ok,Pid} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid),
+ ok = do_sync_func_test(Pid),
+ ok = do_func_test(Name),
+ ok = do_sync_func_test(Name),
+ stop_it(Pid),
+
+ %%process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq().
+
+%% local register linked
+start9(Config) ->
+ %%OldFl = process_flag(trap_exit, true),
+ Name = my_stm,
+ STM = {local,Name},
+
+ {ok,Pid} =
+ gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid),
+ ok = do_sync_func_test(Pid),
+ ok = do_func_test(Name),
+ ok = do_sync_func_test(Name),
+ stop_it(Pid),
+
+ %%process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq().
+
+%% global register
+start10(Config) ->
+ STM = {global,my_stm},
+
+ {ok,Pid} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid),
+ ok = do_sync_func_test(Pid),
+ ok = do_func_test(STM),
+ ok = do_sync_func_test(STM),
+ stop_it(STM),
+
+ ok = verify_empty_msgq().
+
+%% Stop registered processes
+start11(Config) ->
+ Name = my_stm,
+ LocalSTM = {local,Name},
+ GlobalSTM = {global,Name},
+
+ {ok,Pid} =
+ gen_statem:start_link(LocalSTM, ?MODULE, start_arg(Config, []), []),
+ stop_it(Pid),
+
+ {ok,_Pid1} =
+ gen_statem:start_link(LocalSTM, ?MODULE, start_arg(Config, []), []),
+ stop_it(Name),
+
+ {ok,Pid2} =
+ gen_statem:start(GlobalSTM, ?MODULE, start_arg(Config, []), []),
+ stop_it(Pid2),
+ receive after 1 -> true end,
+ Result =
+ gen_statem:start(GlobalSTM, ?MODULE, start_arg(Config, []), []),
+ ct:log("Result = ~p~n",[Result]),
+ {ok,_Pid3} = Result,
+ stop_it(GlobalSTM),
+
+ ok = verify_empty_msgq().
+
+%% Via register linked
+start12(Config) ->
+ dummy_via:reset(),
+ VIA = {via,dummy_via,my_stm},
+
+ {ok,Pid} =
+ gen_statem:start_link(VIA, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start_link(VIA, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start(VIA, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid),
+ ok = do_sync_func_test(Pid),
+ ok = do_func_test(VIA),
+ ok = do_sync_func_test(VIA),
+ stop_it(VIA),
+
+ ok = verify_empty_msgq().
+
+
+%% Anonymous, reason 'normal'
+stop1(Config) ->
+ {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ ok = gen_statem:stop(Pid),
+ false = erlang:is_process_alive(Pid),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(Pid), Reason).
+
+%% Anonymous, other reason
+stop2(Config) ->
+ {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ ok = gen_statem:stop(Pid, other_reason, infinity),
+ false = erlang:is_process_alive(Pid),
+ ok.
+
+%% Anonymous, invalid timeout
+stop3(Config) ->
+ {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ _ =
+ ?EXPECT_FAILURE(
+ gen_statem:stop(Pid, other_reason, invalid_timeout),
+ Reason),
+ true = erlang:is_process_alive(Pid),
+ ok = gen_statem:stop(Pid),
+ false = erlang:is_process_alive(Pid),
+ ok.
+
+%% Registered name
+stop4(Config) ->
+ {ok,Pid} =
+ gen_statem:start(
+ {local,to_stop},?MODULE, start_arg(Config, []), []),
+ ok = gen_statem:stop(to_stop),
+ false = erlang:is_process_alive(Pid),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(to_stop), Reason),
+ ok.
+
+%% Registered name and local node
+stop5(Config) ->
+ Name = to_stop,
+ {ok,Pid} =
+ gen_statem:start(
+ {local,Name},?MODULE, start_arg(Config, []), []),
+ ok = gen_statem:stop({Name,node()}),
+ false = erlang:is_process_alive(Pid),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop({Name,node()}), Reason),
+ ok.
+
+%% Globally registered name
+stop6(Config) ->
+ STM = {global,to_stop},
+ {ok,Pid} = gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ ok = gen_statem:stop(STM),
+ false = erlang:is_process_alive(Pid),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(STM), Reason),
+ ok.
+
+%% 'via' registered name
+stop7(Config) ->
+ VIA = {via,dummy_via,to_stop},
+ dummy_via:reset(),
+ {ok,Pid} = gen_statem:start(VIA, ?MODULE, start_arg(Config, []), []),
+ ok = gen_statem:stop(VIA),
+ false = erlang:is_process_alive(Pid),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(VIA), Reason),
+ ok.
+
+%% Anonymous on remote node
+stop8(Config) ->
+ Node = gen_statem_stop8,
+ {ok,NodeName} = ct_slave:start(Node),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(NodeName, code, add_path, [Dir]),
+ {ok,Pid} =
+ rpc:call(
+ NodeName, gen_statem,start,
+ [?MODULE,start_arg(Config, []),[]]),
+ ok = gen_statem:stop(Pid),
+ false = rpc:call(NodeName, erlang, is_process_alive, [Pid]),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(Pid), Reason1),
+ {ok,NodeName} = ct_slave:stop(Node),
+ {{nodedown,NodeName},{sys,terminate,_}} =
+ ?EXPECT_FAILURE(gen_statem:stop(Pid), Reason2),
+ ok.
+
+%% Registered name on remote node
+stop9(Config) ->
+ Name = to_stop,
+ LocalSTM = {local,Name},
+ Node = gen_statem__stop9,
+ {ok,NodeName} = ct_slave:start(Node),
+ STM = {Name,NodeName},
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(NodeName, code, add_path, [Dir]),
+ {ok,Pid} =
+ rpc:call(
+ NodeName, gen_statem, start,
+ [LocalSTM,?MODULE,start_arg(Config, []),[]]),
+ ok = gen_statem:stop(STM),
+ undefined = rpc:call(NodeName,erlang,whereis,[Name]),
+ false = rpc:call(NodeName,erlang,is_process_alive,[Pid]),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(STM), Reason1),
+ {ok,NodeName} = ct_slave:stop(Node),
+ {{nodedown,NodeName},{sys,terminate,_}} =
+ ?EXPECT_FAILURE(gen_statem:stop(STM), Reason2),
+ ok.
+
+%% Globally registered name on remote node
+stop10(Config) ->
+ Node = gen_statem_stop10,
+ STM = {global,to_stop},
+ {ok,NodeName} = ct_slave:start(Node),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(NodeName,code,add_path,[Dir]),
+ {ok,Pid} =
+ rpc:call(
+ NodeName, gen_statem, start,
+ [STM,?MODULE,start_arg(Config, []),[]]),
+ global:sync(),
+ ok = gen_statem:stop(STM),
+ false = rpc:call(NodeName, erlang, is_process_alive, [Pid]),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(STM), Reason1),
+ {ok,NodeName} = ct_slave:stop(Node),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(STM), Reason2),
+ ok.
+
+%% Check that time outs in calls work
+abnormal1(Config) ->
+ Name = abnormal1,
+ LocalSTM = {local,Name},
+
+ {ok, _Pid} =
+ gen_statem:start(LocalSTM, ?MODULE, start_arg(Config, []), []),
+
+ %% timeout call.
+ delayed = gen_statem:call(Name, {delayed_answer,1}, 100),
+ {timeout,_} =
+ ?EXPECT_FAILURE(
+ gen_statem:call(Name, {delayed_answer,1000}, 10),
+ Reason),
+ ok = gen_statem:stop(Name),
+ ok = verify_empty_msgq().
+
+%% Check that bad return values makes the stm crash. Note that we must
+%% trap exit since we must link to get the real bad_return_ error
+abnormal2(Config) ->
+ OldFl = process_flag(trap_exit, true),
+ {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
+
+ %% bad return value in the gen_statem loop
+ {{bad_return_value,badreturn},_} =
+ ?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
+ receive
+ {'EXIT',Pid,{bad_return_value,badreturn}} -> ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq().
+
+shutdown(Config) ->
+ process_flag(trap_exit, true),
+
+ {ok,Pid0} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
+ ok = do_func_test(Pid0),
+ ok = do_sync_func_test(Pid0),
+ stopped = gen_statem:call(Pid0, {stop,{shutdown,reason}}),
+ receive {'EXIT',Pid0,{shutdown,reason}} -> ok end,
+ process_flag(trap_exit, false),
+
+ {noproc,_} =
+ ?EXPECT_FAILURE(gen_statem:call(Pid0, hej), Reason),
+
+ receive
+ Any ->
+ ct:log("Unexpected: ~p", [Any]),
+ ct:fail({unexpected,Any})
+ after 500 ->
+ ok
+ end.
+
+
+
+stop_and_reply(_Config) ->
+ process_flag(trap_exit, true),
+
+ Machine =
+ %% Abusing the internal format of From...
+ #{init =>
+ fun () ->
+ {ok,start,undefined}
+ end,
+ start =>
+ fun (cast, {echo,From1,Reply1}, undefined) ->
+ {next_state,wait,{reply,From1,Reply1}}
+ end,
+ wait =>
+ fun (cast, {stop_and_reply,Reason,From2,Reply2},R1) ->
+ {stop_and_reply,Reason,
+ [R1,{reply,From2,Reply2}]}
+ end},
+ {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine}, []),
+
+ Self = self(),
+ Tag1 = make_ref(),
+ gen_statem:cast(STM, {echo,{Self,Tag1},reply1}),
+ Tag2 = make_ref(),
+ gen_statem:cast(STM, {stop_and_reply,reason,{Self,Tag2},reply2}),
+ case flush() of
+ [{Tag1,reply1},{Tag2,reply2},{'EXIT',STM,reason}] ->
+ ok;
+ Other1 ->
+ ct:fail({unexpected,Other1})
+ end,
+
+ {noproc,_} =
+ ?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
+ case flush() of
+ [] ->
+ ok;
+ Other2 ->
+ ct:fail({unexpected,Other2})
+ end.
+
+
+
+event_order(_Config) ->
+ process_flag(trap_exit, true),
+
+ Machine =
+ %% Abusing the internal format of From...
+ #{init =>
+ fun () ->
+ {ok,start,undefined}
+ end,
+ start =>
+ fun (cast, _, _) ->
+ {keep_state_and_data,postpone}; %% Handled in 'buffer'
+ ({call,From}, {buffer,Pid,[Tag3,Tag4,Tag5]},
+ undefined) ->
+ {next_state,buffer,[],
+ [{next_event,internal,{reply,{Pid,Tag3},ok3}},
+ {next_event,internal,{reply,{Pid,Tag4},ok4}},
+ {timeout,0,{reply,{Pid,Tag5},ok5}},
+ %% The timeout should not happen since there
+ %% are events that cancel it i.e next_event
+ %% and postponed
+ {reply,From,ok}]}
+ end,
+ buffer =>
+ fun (internal, Reply, Replies) ->
+ {keep_state,[Reply|Replies]};
+ (timeout, Reply, Replies) ->
+ {keep_state,[Reply|Replies]};
+ (cast, Reply, Replies) ->
+ {keep_state,[Reply|Replies]};
+ ({call,From}, {stop,Reason}, Replies) ->
+ {next_state,stop,undefined,
+ lists:reverse(
+ Replies,
+ [{reply,From,ok},
+ {next_event,internal,{stop,Reason}}])}
+ end,
+ stop =>
+ fun (internal, Result, undefined) ->
+ Result
+ end},
+
+ {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine}, []),
+ Self = self(),
+ Tag1 = make_ref(),
+ gen_statem:cast(STM, {reply,{Self,Tag1},ok1}),
+ Tag2 = make_ref(),
+ gen_statem:cast(STM, {reply,{Self,Tag2},ok2}),
+ Tag3 = make_ref(),
+ Tag4 = make_ref(),
+ Tag5 = make_ref(),
+ ok = gen_statem:call(STM, {buffer,Self,[Tag3,Tag4,Tag5]}),
+ ok = gen_statem:call(STM, {stop,reason}),
+ case flush() of
+ [{Tag3,ok3},{Tag4,ok4},{Tag1,ok1},{Tag2,ok2},
+ {'EXIT',STM,reason}] ->
+ ok;
+ Other1 ->
+ ct:fail({unexpected,Other1})
+ end,
+
+ {noproc,_} =
+ ?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
+ case flush() of
+ [] ->
+ ok;
+ Other2 ->
+ ct:fail({unexpected,Other2})
+ end.
+
+
+
+sys1(Config) ->
+ {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ {status, Pid, {module,gen_statem}, _} = sys:get_status(Pid),
+ sys:suspend(Pid),
+ Parent = self(),
+ Tag = make_ref(),
+ Caller =
+ spawn(
+ fun () ->
+ Parent ! {Tag,gen_statem:call(Pid, hej)}
+ end),
+ receive
+ {Tag,_} ->
+ ct:fail(should_be_suspended)
+ after 3000 ->
+ exit(Caller, ok)
+ end,
+
+ %% {timeout,_} =
+ %% ?EXPECT_FAILURE(gen_statem:call(Pid, hej), Reason),
+ sys:resume(Pid),
+ stop_it(Pid).
+
+code_change(Config) ->
+ Mode = handle_event_function,
+ {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ {idle,data} = sys:get_state(Pid),
+ sys:suspend(Pid),
+ sys:change_code(Pid, ?MODULE, old_vsn, Mode),
+ sys:resume(Pid),
+ {idle,{old_vsn,data,Mode}} = sys:get_state(Pid),
+ Mode = gen_statem:call(Pid, get_callback_mode),
+ stop_it(Pid).
+
+call_format_status(Config) ->
+ {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ Status = sys:get_status(Pid),
+ {status,Pid,_Mod,[_PDict,running,_,_, Data]} = Status,
+ [format_status_called|_] = lists:reverse(Data),
+ stop_it(Pid),
+
+ %% check that format_status can handle a name being an atom (pid is
+ %% already checked by the previous test)
+ {ok, Pid2} =
+ gen_statem:start(
+ {local, gstm}, ?MODULE, start_arg(Config, []), []),
+ Status2 = sys:get_status(gstm),
+ {status,Pid2,_Mod,[_PDict2,running,_,_,Data2]} = Status2,
+ [format_status_called|_] = lists:reverse(Data2),
+ stop_it(Pid2),
+
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ GlobalName1 = {global,"CallFormatStatus"},
+ {ok,Pid3} =
+ gen_statem:start(
+ GlobalName1, ?MODULE, start_arg(Config, []), []),
+ Status3 = sys:get_status(GlobalName1),
+ {status,Pid3,_Mod,[_PDict3,running,_,_,Data3]} = Status3,
+ [format_status_called|_] = lists:reverse(Data3),
+ stop_it(Pid3),
+ GlobalName2 = {global,{name, "term"}},
+ {ok,Pid4} =
+ gen_statem:start(
+ GlobalName2, ?MODULE, start_arg(Config, []), []),
+ Status4 = sys:get_status(GlobalName2),
+ {status,Pid4,_Mod,[_PDict4,running,_,_, Data4]} = Status4,
+ [format_status_called|_] = lists:reverse(Data4),
+ stop_it(Pid4),
+
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ dummy_via:reset(),
+ ViaName1 = {via,dummy_via,"CallFormatStatus"},
+ {ok,Pid5} = gen_statem:start(ViaName1, ?MODULE, start_arg(Config, []), []),
+ Status5 = sys:get_status(ViaName1),
+ {status,Pid5,_Mod, [_PDict5,running,_,_, Data5]} = Status5,
+ [format_status_called|_] = lists:reverse(Data5),
+ stop_it(Pid5),
+ ViaName2 = {via,dummy_via,{name,"term"}},
+ {ok, Pid6} =
+ gen_statem:start(
+ ViaName2, ?MODULE, start_arg(Config, []), []),
+ Status6 = sys:get_status(ViaName2),
+ {status,Pid6,_Mod,[_PDict6,running,_,_,Data6]} = Status6,
+ [format_status_called|_] = lists:reverse(Data6),
+ stop_it(Pid6).
+
+
+
+error_format_status(Config) ->
+ error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ Data = "called format_status",
+ {ok,Pid} =
+ gen_statem:start(
+ ?MODULE, start_arg(Config, {data,Data}), []),
+ %% bad return value in the gen_statem loop
+ {{bad_return_value,badreturn},_} =
+ ?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
+ receive
+ {error,_,
+ {Pid,
+ "** State machine"++_,
+ [Pid,{{call,_},badreturn},
+ {formatted,idle,Data},
+ error,{bad_return_value,badreturn}|_]}} ->
+ ok;
+ Other when is_tuple(Other), element(1, Other) =:= error ->
+ error_logger_forwarder:unregister(),
+ ct:fail({unexpected,Other})
+ after 1000 ->
+ error_logger_forwarder:unregister(),
+ ct:fail(timeout)
+ end,
+ process_flag(trap_exit, OldFl),
+ error_logger_forwarder:unregister(),
+ receive
+ %% Comes with SASL
+ {error_report,_,{Pid,crash_report,_}} ->
+ ok
+ after 500 ->
+ ok
+ end,
+ ok = verify_empty_msgq().
+
+terminate_crash_format(Config) ->
+ error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ Data = crash_terminate,
+ {ok,Pid} =
+ gen_statem:start(
+ ?MODULE, start_arg(Config, {data,Data}), []),
+ stop_it(Pid),
+ Self = self(),
+ receive
+ {error,_GroupLeader,
+ {Pid,
+ "** State machine"++_,
+ [Pid,
+ {{call,{Self,_}},stop},
+ {formatted,idle,Data},
+ exit,{crash,terminate}|_]}} ->
+ ok;
+ Other when is_tuple(Other), element(1, Other) =:= error ->
+ error_logger_forwarder:unregister(),
+ ct:fail({unexpected,Other})
+ after 1000 ->
+ error_logger_forwarder:unregister(),
+ ct:fail(timeout)
+ end,
+ process_flag(trap_exit, OldFl),
+ error_logger_forwarder:unregister(),
+ receive
+ %% Comes with SASL
+ {error_report,_,{Pid,crash_report,_}} ->
+ ok
+ after 500 ->
+ ok
+ end,
+ ok = verify_empty_msgq().
+
+
+get_state(Config) ->
+ State = self(),
+ {ok,Pid} =
+ gen_statem:start(
+ ?MODULE, start_arg(Config, {data,State}), []),
+ {idle,State} = sys:get_state(Pid),
+ {idle,State} = sys:get_state(Pid, 5000),
+ stop_it(Pid),
+
+ %% check that get_state can handle a name being an atom (pid is
+ %% already checked by the previous test)
+ {ok,Pid2} =
+ gen_statem:start(
+ {local,gstm}, ?MODULE, start_arg(Config, {data,State}), []),
+ {idle,State} = sys:get_state(gstm),
+ {idle,State} = sys:get_state(gstm, 5000),
+ stop_it(Pid2),
+
+ %% check that get_state works when pid is sys suspended
+ {ok,Pid3} =
+ gen_statem:start(
+ ?MODULE, start_arg(Config, {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 = verify_empty_msgq().
+
+replace_state(Config) ->
+ State = self(),
+ {ok, Pid} =
+ gen_statem:start(
+ ?MODULE, start_arg(Config, {data,State}), []),
+ {idle,State} = sys:get_state(Pid),
+ NState1 = "replaced",
+ Replace1 = fun({StateName, _}) -> {StateName,NState1} end,
+ {idle,NState1} = sys:replace_state(Pid, Replace1),
+ {idle,NState1} = sys:get_state(Pid),
+ NState2 = "replaced again",
+ Replace2 = fun({idle, _}) -> {state0,NState2} end,
+ {state0,NState2} = sys:replace_state(Pid, Replace2, 5000),
+ {state0,NState2} = sys:get_state(Pid),
+ %% verify no change in state if replace function crashes
+ Replace3 = fun(_) -> error(fail) end,
+ {callback_failed,
+ {gen_statem,system_replace_state},{error,fail}} =
+ ?EXPECT_FAILURE(sys:replace_state(Pid, Replace3), Reason),
+ {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 = verify_empty_msgq().
+
+%% Hibernation
+hibernate(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ {ok,Pid0} =
+ gen_statem:start_link(
+ ?MODULE, start_arg(Config, hiber_now), []),
+ is_in_erlang_hibernate(Pid0),
+ stop_it(Pid0),
+ receive
+ {'EXIT',Pid0,normal} -> ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ {ok,Pid} =
+ gen_statem:start_link(?MODULE, start_arg(Config, hiber), []),
+ true = ({current_function,{erlang,hibernate,3}} =/=
+ erlang:process_info(Pid,current_function)),
+ hibernating = gen_statem:call(Pid, hibernate_sync),
+ is_in_erlang_hibernate(Pid),
+ good_morning = gen_statem:call(Pid, wakeup_sync),
+ is_not_in_erlang_hibernate(Pid),
+ hibernating = gen_statem:call(Pid, hibernate_sync),
+ is_in_erlang_hibernate(Pid),
+ please_just_five_more = gen_statem:call(Pid, snooze_sync),
+ is_in_erlang_hibernate(Pid),
+ good_morning = gen_statem:call(Pid, wakeup_sync),
+ is_not_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, hibernate_async),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, wakeup_async),
+ is_not_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, hibernate_async),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, snooze_async),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, wakeup_async),
+ is_not_in_erlang_hibernate(Pid),
+
+ Pid ! hibernate_later,
+ true =
+ ({current_function,{erlang,hibernate,3}} =/=
+ erlang:process_info(Pid, current_function)),
+ is_in_erlang_hibernate(Pid),
+
+ 'alive!' = gen_statem:call(Pid, 'alive?'),
+ true =
+ ({current_function,{erlang,hibernate,3}} =/=
+ erlang:process_info(Pid, current_function)),
+ Pid ! hibernate_now,
+ is_in_erlang_hibernate(Pid),
+
+ 'alive!' = gen_statem:call(Pid, 'alive?'),
+ true =
+ ({current_function,{erlang,hibernate,3}} =/=
+ erlang:process_info(Pid, current_function)),
+
+ hibernating = gen_statem:call(Pid, hibernate_sync),
+ is_in_erlang_hibernate(Pid),
+ good_morning = gen_statem:call(Pid, wakeup_sync),
+ is_not_in_erlang_hibernate(Pid),
+ hibernating = gen_statem:call(Pid, hibernate_sync),
+ is_in_erlang_hibernate(Pid),
+ please_just_five_more = gen_statem:call(Pid, snooze_sync),
+ is_in_erlang_hibernate(Pid),
+ good_morning = gen_statem:call(Pid, wakeup_sync),
+ is_not_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, hibernate_async),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, wakeup_async),
+ is_not_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, hibernate_async),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, snooze_async),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_statem:cast(Pid, wakeup_async),
+ is_not_in_erlang_hibernate(Pid),
+
+ hibernating = gen_statem:call(Pid, hibernate_sync),
+ is_in_erlang_hibernate(Pid),
+ sys:suspend(Pid),
+ is_in_erlang_hibernate(Pid),
+ sys:resume(Pid),
+ is_in_erlang_hibernate(Pid),
+ receive after 1000 -> ok end,
+ is_in_erlang_hibernate(Pid),
+
+ good_morning = gen_statem:call(Pid, wakeup_sync),
+ is_not_in_erlang_hibernate(Pid),
+ stop_it(Pid),
+ process_flag(trap_exit, OldFl),
+ receive
+ {'EXIT',Pid,normal} -> ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+ ok = verify_empty_msgq().
+
+is_in_erlang_hibernate(Pid) ->
+ receive after 1 -> ok end,
+ is_in_erlang_hibernate_1(200, Pid).
+
+is_in_erlang_hibernate_1(0, Pid) ->
+ ct:log("~p\n", [erlang:process_info(Pid, current_function)]),
+ ct:fail(not_in_erlang_hibernate_3);
+is_in_erlang_hibernate_1(N, Pid) ->
+ {current_function,MFA} = erlang:process_info(Pid, current_function),
+ case MFA of
+ {erlang,hibernate,3} ->
+ ok;
+ _ ->
+ receive after 10 -> ok end,
+ is_in_erlang_hibernate_1(N-1, Pid)
+ end.
+
+is_not_in_erlang_hibernate(Pid) ->
+ receive after 1 -> ok end,
+ is_not_in_erlang_hibernate_1(200, Pid).
+
+is_not_in_erlang_hibernate_1(0, Pid) ->
+ ct:log("~p\n", [erlang:process_info(Pid, current_function)]),
+ ct:fail(not_in_erlang_hibernate_3);
+is_not_in_erlang_hibernate_1(N, Pid) ->
+ {current_function,MFA} = erlang:process_info(Pid, current_function),
+ case MFA of
+ {erlang,hibernate,3} ->
+ receive after 10 -> ok end,
+ is_not_in_erlang_hibernate_1(N-1, Pid);
+ _ ->
+ ok
+ end.
+
+
+enter_loop(_Config) ->
+ OldFlag = process_flag(trap_exit, true),
+
+ dummy_via:reset(),
+
+ %% Locally registered process + {local,Name}
+ {ok,Pid1a} =
+ proc_lib:start_link(?MODULE, enter_loop, [local,local]),
+ yes = gen_statem:call(Pid1a, 'alive?'),
+ stopped = gen_statem:call(Pid1a, stop),
+ receive
+ {'EXIT',Pid1a,normal} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ %% Unregistered process + {local,Name}
+ {ok,Pid1b} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon,local]),
+ receive
+ {'EXIT',Pid1b,process_not_registered} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ %% Globally registered process + {global,Name}
+ {ok,Pid2a} =
+ proc_lib:start_link(?MODULE, enter_loop, [global,global]),
+ yes = gen_statem:call(Pid2a, 'alive?'),
+ stopped = gen_statem:call(Pid2a, stop),
+ receive
+ {'EXIT',Pid2a,normal} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ %% Unregistered process + {global,Name}
+ {ok,Pid2b} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon,global]),
+ receive
+ {'EXIT',Pid2b,process_not_registered_globally} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ %% Unregistered process + no name
+ {ok,Pid3} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon,anon]),
+ yes = gen_statem:call(Pid3, 'alive?'),
+ stopped = gen_statem:call(Pid3, stop),
+ receive
+ {'EXIT',Pid3,normal} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ %% Process not started using proc_lib
+ CallbackMode = state_functions,
+ Pid4 =
+ spawn_link(
+ gen_statem, enter_loop,
+ [?MODULE,[],CallbackMode,state0,[]]),
+ receive
+ {'EXIT',Pid4,process_was_not_started_by_proc_lib} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ %% Make sure I am the parent, ie that ordering a shutdown will
+ %% result in the process terminating with Reason==shutdown
+ {ok,Pid5} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon,anon]),
+ yes = gen_statem:call(Pid5, 'alive?'),
+ exit(Pid5, shutdown),
+ receive
+ {'EXIT',Pid5,shutdown} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+
+ %% Make sure gen_statem:enter_loop does not accept {local,Name}
+ %% when it's another process than the calling one which is
+ %% registered under that name
+ register(armitage, self()),
+ {ok,Pid6a} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon,local]),
+ receive
+ {'EXIT',Pid6a,process_not_registered} ->
+ ok
+ after 1000 ->
+ ct:fail(gen_statem_started)
+ end,
+ unregister(armitage),
+
+ %% Make sure gen_statem:enter_loop does not accept {global,Name}
+ %% when it's another process than the calling one which is
+ %% registered under that name
+ global:register_name(armitage, self()),
+ {ok,Pid6b} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon,global]),
+ receive
+ {'EXIT',Pid6b,process_not_registered_globally} ->
+ ok
+ after 1000 ->
+ ct:fail(gen_statem_started)
+ end,
+ global:unregister_name(armitage),
+
+ dummy_via:register_name(armitage, self()),
+ {ok,Pid6c} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon,via]),
+ receive
+ {'EXIT',Pid6c,{process_not_registered_via,dummy_via}} ->
+ ok
+ after 1000 ->
+ ct:fail(
+ {gen_statem_started,
+ process_info(self(), messages)})
+ end,
+ dummy_via:unregister_name(armitage),
+
+ process_flag(trap_exit, OldFlag),
+ ok = verify_empty_msgq().
+
+enter_loop(Reg1, Reg2) ->
+ process_flag(trap_exit, true),
+ case Reg1 of
+ local -> register(armitage, self());
+ global -> global:register_name(armitage, self());
+ via -> dummy_via:register_name(armitage, self());
+ anon -> ignore
+ end,
+ proc_lib:init_ack({ok, self()}),
+ CallbackMode = state_functions,
+ case Reg2 of
+ local ->
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [], {local,armitage});
+ global ->
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [], {global,armitage});
+ via ->
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [],
+ {via, dummy_via, armitage});
+ anon ->
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [])
+ end.
+
+
+%% Test the order for multiple {next_event,T,C}
+next_events(Config) ->
+ {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+ ok = gen_statem:cast(Pid, next_event),
+ {state,next_events,[]} = gen_statem:call(Pid, get),
+ ok = gen_statem:stop(Pid),
+ false = erlang:is_process_alive(Pid),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(Pid), Reason).
+
+
+%%
+%% Functionality check
+%%
+
+wfor(Msg) ->
+ receive
+ Msg -> ok
+ after 5000 ->
+ error(timeout)
+ end.
+
+
+stop_it(STM) ->
+ stopped = gen_statem:call(STM, stop),
+ check_stopped(STM).
+
+
+check_stopped(STM) ->
+ Call = there_you_are,
+ {_,{gen_statem,call,[_,Call,infinity]}} =
+ ?EXPECT_FAILURE(gen_statem:call(STM, Call), Reason),
+ ok.
+
+
+do_func_test(STM) ->
+ ok = gen_statem:cast(STM, {'alive?',self()}),
+ wfor(yes),
+ ok = do_connect(STM),
+ ok = gen_statem:cast(STM, {'alive?',self()}),
+ wfor(yes),
+ ?t:do_times(3, ?MODULE, do_msg, [STM]),
+ ok = gen_statem:cast(STM, {'alive?',self()}),
+ wfor(yes),
+ ok = do_disconnect(STM),
+ ok = gen_statem:cast(STM, {'alive?',self()}),
+ wfor(yes),
+ ok.
+
+
+do_connect(STM) ->
+ check_state(STM, idle),
+ gen_statem:cast(STM, {connect,self()}),
+ wfor(accept),
+ check_state(STM, wfor_conf),
+ Tag = make_ref(),
+ gen_statem:cast(STM, {ping,self(),Tag}),
+ gen_statem:cast(STM, confirm),
+ wfor({pong,Tag}),
+ check_state(STM, connected),
+ ok.
+
+do_msg(STM) ->
+ check_state(STM, connected),
+ R = make_ref(),
+ ok = gen_statem:cast(STM, {msg,self(),R}),
+ wfor({ack,R}).
+
+
+do_disconnect(STM) ->
+ ok = gen_statem:cast(STM, disconnect),
+ check_state(STM, idle).
+
+check_state(STM, State) ->
+ case gen_statem:call(STM, get) of
+ {state, State, _} -> ok
+ end.
+
+do_sync_func_test(STM) ->
+ yes = gen_statem:call(STM, 'alive?'),
+ ok = do_sync_connect(STM),
+ yes = gen_statem:call(STM, 'alive?'),
+ ?t:do_times(3, ?MODULE, do_sync_msg, [STM]),
+ yes = gen_statem:call(STM, 'alive?'),
+ ok = do_sync_disconnect(STM),
+ yes = gen_statem:call(STM, 'alive?'),
+ check_state(STM, idle),
+ ok = gen_statem:call(STM, {timeout,200}),
+ yes = gen_statem:call(STM, 'alive?'),
+ check_state(STM, idle),
+ ok.
+
+
+do_sync_connect(STM) ->
+ check_state(STM, idle),
+ accept = gen_statem:call(STM, connect),
+ check_state(STM, wfor_conf),
+ Tag = make_ref(),
+ gen_statem:cast(STM, {ping,self(),Tag}),
+ yes = gen_statem:call(STM, confirm),
+ wfor({pong,Tag}),
+ check_state(STM, connected),
+ ok.
+
+do_sync_msg(STM) ->
+ check_state(STM, connected),
+ R = make_ref(),
+ {ack,R} = gen_statem:call(STM, {msg,R}),
+ ok.
+
+do_sync_disconnect(STM) ->
+ yes = gen_statem:call(STM, disconnect),
+ check_state(STM, idle).
+
+
+verify_empty_msgq() ->
+ [] = flush(),
+ ok.
+
+start_arg(Config, Arg) ->
+ case lists:keyfind(callback_mode, 1, Config) of
+ {_,CallbackMode} ->
+ {callback_mode,CallbackMode,Arg};
+ false ->
+ Arg
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% The State Machine
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init(ignore) ->
+ ignore;
+init(stop) ->
+ {stop,stopped};
+init(stop_shutdown) ->
+ {stop,shutdown};
+init(sleep) ->
+ ?t:sleep(1000),
+ {state_functions,idle,data};
+init(hiber) ->
+ {state_functions,hiber_idle,[]};
+init(hiber_now) ->
+ {state_functions,hiber_idle,[],[hibernate]};
+init({data, Data}) ->
+ {state_functions,idle,Data};
+init({callback_mode,CallbackMode,Arg}) ->
+ case init(Arg) of
+ {_,State,Data,Ops} ->
+ {CallbackMode,State,Data,Ops};
+ {_,State,Data} ->
+ {CallbackMode,State,Data};
+ Other ->
+ Other
+ end;
+init({map_statem,#{init := Init}=Machine}) ->
+ case Init() of
+ {ok,State,Data,Ops} ->
+ {handle_event_function,State,[Data|Machine],Ops};
+ {ok,State,Data} ->
+ {handle_event_function,State,[Data|Machine]};
+ Other ->
+ Other
+ end;
+init([]) ->
+ {state_functions,idle,data}.
+
+terminate(_, _State, crash_terminate) ->
+ exit({crash,terminate});
+terminate({From,stopped}, State, _Data) ->
+ From ! {self(),{stopped,State}},
+ ok;
+terminate(_Reason, _State, _Data) ->
+ ok.
+
+
+%% State functions
+
+idle(cast, {connect,Pid}, Data) ->
+ Pid ! accept,
+ {next_state,wfor_conf,Data,infinity}; % NoOp timeout just to test API
+idle({call,From}, connect, Data) ->
+ gen_statem:reply(From, accept),
+ {next_state,wfor_conf,Data,infinity}; % NoOp timeout just to test API
+idle(cast, badreturn, _Data) ->
+ badreturn;
+idle({call,_From}, badreturn, _Data) ->
+ badreturn;
+idle({call,From}, {delayed_answer,T}, Data) ->
+ receive
+ after T ->
+ gen_statem:reply({reply,From,delayed}),
+ throw({keep_state,Data})
+ end;
+idle({call,From}, {timeout,Time}, _Data) ->
+ {next_state,timeout,{From,Time},
+ {timeout,Time,idle}};
+idle(cast, next_event, _Data) ->
+ {next_state,next_events,[a,b,c],
+ [{next_event,internal,a},
+ {next_event,internal,b},
+ {next_event,internal,c}]};
+idle(Type, Content, Data) ->
+ case handle_common_events(Type, Content, idle, Data) of
+ undefined ->
+ case Type of
+ {call,From} ->
+ throw({keep_state,Data,[{reply,From,'eh?'}]});
+ _ ->
+ throw(
+ {stop,{unexpected,idle,Type,Content}})
+ end;
+ Result ->
+ Result
+ end.
+
+timeout(timeout, idle, {From,Time}) ->
+ TRef = erlang:start_timer(Time, self(), ok),
+ {keep_state,{From,TRef},0}; % Immediate timeout 0
+timeout(timeout, 0, {From,TRef}) ->
+ {next_state,timeout2,{From,TRef},
+ [{timeout,1,should_be_cancelled},
+ postpone]}; % Should cancel state timeout
+timeout(_, _, _) ->
+ keep_state_and_data.
+
+timeout2(timeout, 0, _) ->
+ keep_state_and_data;
+timeout2(timeout, Reason, _) ->
+ {stop,Reason};
+timeout2(info, {timeout,TRef,Result}, {From,TRef}) ->
+ gen_statem:reply([{reply,From,Result}]),
+ {next_state,idle,state};
+timeout2(_, _, _) ->
+ {keep_state_and_data,[]}.
+
+wfor_conf({call,From}, confirm, Data) ->
+ {next_state,connected,Data,
+ {reply,From,yes}};
+wfor_conf(cast, {ping,_,_}, _) ->
+ {keep_state_and_data,[postpone]};
+wfor_conf(cast, confirm, Data) ->
+ {next_state,connected,Data};
+wfor_conf(Type, Content, Data) ->
+ case handle_common_events(Type, Content, wfor_conf, Data) of
+ undefined ->
+ case Type of
+ {call,From} ->
+ {next_state,idle,Data,
+ [{reply,From,'eh?'}]};
+ _ ->
+ throw(keep_state_and_data)
+ end;
+ Result ->
+ Result
+ end.
+
+connected({call,From}, {msg,Ref}, Data) ->
+ {keep_state,Data,
+ {reply,From,{ack,Ref}}};
+connected(cast, {msg,From,Ref}, Data) ->
+ From ! {ack,Ref},
+ {keep_state,Data};
+connected({call,From}, disconnect, Data) ->
+ {next_state,idle,Data,
+ [{reply,From,yes}]};
+connected(cast, disconnect, Data) ->
+ {next_state,idle,Data};
+connected(cast, {ping,Pid,Tag}, Data) ->
+ Pid ! {pong,Tag},
+ {keep_state,Data};
+connected(Type, Content, Data) ->
+ case handle_common_events(Type, Content, connected, Data) of
+ undefined ->
+ case Type of
+ {call,From} ->
+ {keep_state,Data,
+ [{reply,From,'eh?'}]};
+ _ ->
+ {keep_state,Data}
+ end;
+ Result ->
+ Result
+ end.
+
+state0({call,From}, stop, Data) ->
+ {stop_and_reply,normal,[{reply,From,stopped}],Data};
+state0(Type, Content, Data) ->
+ case handle_common_events(Type, Content, state0, Data) of
+ undefined ->
+ {keep_state,Data};
+ Result ->
+ Result
+ end.
+
+hiber_idle({call,From}, 'alive?', Data) ->
+ {keep_state,Data,
+ [{reply,From,'alive!'}]};
+hiber_idle({call,From}, hibernate_sync, Data) ->
+ {next_state,hiber_wakeup,Data,
+ [{reply,From,hibernating},
+ hibernate]};
+hiber_idle(info, hibernate_later, _) ->
+ Tref = erlang:start_timer(1000, self(), hibernate),
+ {keep_state,Tref};
+hiber_idle(info, hibernate_now, Data) ->
+ {keep_state,Data,
+ [hibernate]};
+hiber_idle(info, {timeout,Tref,hibernate}, Tref) ->
+ {keep_state,[],
+ [hibernate]};
+hiber_idle(cast, hibernate_async, Data) ->
+ {next_state,hiber_wakeup,Data,
+ [hibernate]};
+hiber_idle(Type, Content, Data) ->
+ case handle_common_events(Type, Content, hiber_idle, Data) of
+ undefined ->
+ {keep_state,Data};
+ Result ->
+ Result
+ end.
+
+hiber_wakeup({call,From}, wakeup_sync, Data) ->
+ {next_state,hiber_idle,Data,
+ [{reply,From,good_morning}]};
+hiber_wakeup({call,From}, snooze_sync, Data) ->
+ {keep_state,Data,
+ [{reply,From,please_just_five_more},
+ hibernate]};
+hiber_wakeup(cast, wakeup_async, Data) ->
+ {next_state,hiber_idle,Data};
+hiber_wakeup(cast, snooze_async, Data) ->
+ {keep_state,Data,
+ [hibernate]};
+hiber_wakeup(Type, Content, Data) ->
+ case handle_common_events(Type, Content, hiber_wakeup, Data) of
+ undefined ->
+ {keep_state,Data};
+ Result ->
+ Result
+ end.
+
+next_events(internal, Msg, [Msg|Msgs]) ->
+ {keep_state,Msgs};
+next_events(Type, Content, Data) ->
+ case handle_common_events(Type, Content, next_events, Data) of
+ undefined ->
+ {keep_state,Data};
+ Result ->
+ Result
+ end.
+
+
+handle_common_events({call,From}, get_callback_mode, _, _) ->
+ {keep_state_and_data,{reply,From,state_functions}};
+handle_common_events({call,From}, get, State, Data) ->
+ {keep_state,Data,
+ [{reply,From,{state,State,Data}}]};
+handle_common_events(cast, {get,Pid}, State, Data) ->
+ Pid ! {state,State,Data},
+ {keep_state,Data};
+handle_common_events({call,From}, stop, _, Data) ->
+ {stop_and_reply,normal,[{reply,From,stopped}],Data};
+handle_common_events(cast, stop, _, _) ->
+ stop;
+handle_common_events({call,From}, {stop,Reason}, _, Data) ->
+ {stop_and_reply,Reason,{reply,From,stopped},Data};
+handle_common_events(cast, {stop,Reason}, _, _) ->
+ {stop,Reason};
+handle_common_events({call,From}, 'alive?', _, Data) ->
+ {keep_state,Data,
+ [{reply,From,yes}]};
+handle_common_events(cast, {'alive?',Pid}, _, Data) ->
+ Pid ! yes,
+ {keep_state,Data};
+handle_common_events(_, _, _, _) ->
+ undefined.
+
+handle_event({call,From}, get_callback_mode, _, _) ->
+ {keep_state_and_data,{reply,From,handle_event_function}};
+%% Wrapper state machine that uses a map state machine spec
+handle_event(
+ Type, Event, State, [Data|Machine])
+ when is_map(Machine) ->
+ #{State := HandleEvent} = Machine,
+ case
+ try HandleEvent(Type, Event, Data) of
+ Result ->
+ Result
+ catch
+ Result ->
+ Result
+ end of
+ {stop,Reason,NewData} ->
+ {stop,Reason,[NewData|Machine]};
+ {next_state,NewState,NewData} ->
+ {next_state,NewState,[NewData|Machine]};
+ {next_state,NewState,NewData,Ops} ->
+ {next_state,NewState,[NewData|Machine],Ops};
+ {keep_state,NewData} ->
+ {keep_state,[NewData|Machine]};
+ {keep_state,NewData,Ops} ->
+ {keep_state,[NewData|Machine],Ops};
+ Other ->
+ Other
+ end;
+%%
+%% Dispatcher to test callback_mode handle_event_function
+%%
+%% Wrap the state in a 1 element list just to test non-atom
+%% states. Note that the state from init/1 is not wrapped
+%% so both atom and non-atom states are tested.
+handle_event(Type, Event, State, Data) ->
+ StateName = unwrap_state(State),
+ try ?MODULE:StateName(Type, Event, Data) of
+ Result ->
+ wrap_result(Result)
+ catch
+ throw:Result ->
+ erlang:raise(
+ throw, wrap_result(Result), erlang:get_stacktrace())
+ end.
+
+unwrap_state([State]) ->
+ State;
+unwrap_state(State) ->
+ State.
+
+wrap_result(Result) ->
+ case Result of
+ {next_state,NewState,NewData} ->
+ {next_state,[NewState],NewData};
+ {next_state,NewState,NewData,StateOps} ->
+ {next_state,[NewState],NewData,StateOps};
+ Other ->
+ Other
+ end.
+
+
+
+code_change(OldVsn, State, Data, CallbackMode) ->
+ {CallbackMode,State,{OldVsn,Data,CallbackMode}}.
+
+format_status(terminate, [_Pdict,State,Data]) ->
+ {formatted,State,Data};
+format_status(normal, [_Pdict,_State,_Data]) ->
+ [format_status_called].
+
+flush() ->
+ receive
+ Msg ->
+ [Msg|flush()]
+ after 500 ->
+ []
+ end.
diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl
index 6f2a510f65..531e97e8d6 100644
--- a/lib/stdlib/test/lists_SUITE.erl
+++ b/lib/stdlib/test/lists_SUITE.erl
@@ -55,6 +55,7 @@
ufunsort_error/1,
zip_unzip/1, zip_unzip3/1, zipwith/1, zipwith3/1,
filter_partition/1,
+ join/1,
otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1,
suffix/1, subtract/1, droplast/1, hof/1]).
@@ -119,7 +120,7 @@ groups() ->
{tickets, [parallel], [otp_5939, otp_6023, otp_6606, otp_7230]},
{zip, [parallel], [zip_unzip, zip_unzip3, zipwith, zipwith3]},
{misc, [parallel], [reverse, member, dropwhile, takewhile,
- filter_partition, suffix, subtract,
+ filter_partition, suffix, subtract, join,
hof]}
].
@@ -2413,6 +2414,19 @@ zipwith3(Config) when is_list(Config) ->
ok.
+%% Test lists:join/2
+join(Config) when is_list(Config) ->
+ A = [a,b,c],
+ Sep = x,
+ [a,x,b,x,c] = lists:join(Sep, A),
+
+ B = [b],
+ [b] = lists:join(Sep, B),
+
+ C = [],
+ [] = lists:join(Sep, C),
+ ok.
+
%% Test lists:filter/2, lists:partition/2.
filter_partition(Config) when is_list(Config) ->
F = fun(I) -> I rem 2 =:= 0 end,
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index 8b3a8d7ae2..42e669a799 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -25,15 +25,10 @@
-include_lib("common_test/include/ct.hrl").
-%% Test server specific exports
--export([all/0]).
--export([suite/0]).
--export([init_per_suite/1]).
--export([end_per_suite/1]).
--export([init_per_testcase/2]).
--export([end_per_testcase/2]).
-
--export([t_get_3/1, t_filter_2/1,
+-export([all/0, suite/0]).
+
+-export([t_update_with_3/1, t_update_with_4/1,
+ t_get_3/1, t_filter_2/1,
t_fold_3/1,t_map_2/1,t_size_1/1,
t_with_2/1,t_without_2/1]).
@@ -41,29 +36,56 @@
%%-define(badarg(F,Args), {'EXIT', {badarg, [{maps,F,Args,_}|_]}}).
%% silly broken hipe
-define(badmap(V,F,_Args), {'EXIT', {{badmap,V}, [{maps,F,_,_}|_]}}).
+-define(badkey(K,F,_Args), {'EXIT', {{badkey,K}, [{maps,F,_,_}|_]}}).
-define(badarg(F,_Args), {'EXIT', {badarg, [{maps,F,_,_}|_]}}).
suite() ->
- [{ct_hooks, [ts_install_cth]},
+ [{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,1}}].
all() ->
- [t_get_3,t_filter_2,
+ [t_update_with_3,t_update_with_4,
+ t_get_3,t_filter_2,
t_fold_3,t_map_2,t_size_1,
t_with_2,t_without_2].
-init_per_suite(Config) ->
- Config.
+t_update_with_3(Config) when is_list(Config) ->
+ V1 = value1,
+ V2 = <<"value2">>,
+ V3 = "value3",
+ Map = #{ key1 => V1, key2 => V2, "key3" => V3 },
+ Fun = fun(V) -> [V,V,{V,V}] end,
+
+ #{ key1 := [V1,V1,{V1,V1}] } = maps:update_with(key1,Fun,Map),
+ #{ key2 := [V2,V2,{V2,V2}] } = maps:update_with(key2,Fun,Map),
+ #{ "key3" := [V3,V3,{V3,V3}] } = maps:update_with("key3",Fun,Map),
-end_per_suite(_Config) ->
+ %% error case
+ ?badmap(b,update_with,[[a,b],a,b]) = (catch maps:update_with([a,b],id(a),b)),
+ ?badarg(update_with,[[a,b],a,#{}]) = (catch maps:update_with([a,b],id(a),#{})),
+ ?badkey([a,b],update_with,[[a,b],Fun,#{}]) = (catch maps:update_with([a,b],Fun,#{})),
ok.
-init_per_testcase(_Case, Config) ->
- Config.
+t_update_with_4(Config) when is_list(Config) ->
+ V1 = value1,
+ V2 = <<"value2">>,
+ V3 = "value3",
+ Map = #{ key1 => V1, key2 => V2, "key3" => V3 },
+ Fun = fun(V) -> [V,V,{V,V}] end,
+ Init = 3,
+
+ #{ key1 := [V1,V1,{V1,V1}] } = maps:update_with(key1,Fun,Init,Map),
+ #{ key2 := [V2,V2,{V2,V2}] } = maps:update_with(key2,Fun,Init,Map),
+ #{ "key3" := [V3,V3,{V3,V3}] } = maps:update_with("key3",Fun,Init,Map),
-end_per_testcase(_Case, _Config) ->
+ #{ key3 := Init } = maps:update_with(key3,Fun,Init,Map),
+
+ %% error case
+ ?badmap(b,update_with,[[a,b],a,b]) = (catch maps:update_with([a,b],id(a),b)),
+ ?badarg(update_with,[[a,b],a,#{}]) = (catch maps:update_with([a,b],id(a),#{})),
ok.
+
t_get_3(Config) when is_list(Config) ->
Map = #{ key1 => value1, key2 => value2 },
DefaultValue = "Default value",
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index 3fd5ed4ccf..1bcdc3ccd0 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -377,7 +377,7 @@ crypto_seed() ->
crypto_next(<<Num:64, Bin/binary>>) ->
{Num, Bin};
crypto_next(_) ->
- crypto_next(crypto:rand_bytes((64 div 8)*100)).
+ crypto_next(crypto:strong_rand_bytes((64 div 8)*100)).
crypto_uniform({Api, Data0}) ->
{Int, Data} = crypto_next(Data0),
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index 81272e62de..119d375746 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -38,7 +38,7 @@
follow/3, empty/0]).
-import(erl_parse, [preop_prec/1, inop_prec/1, func_prec/0,
- max_prec/0]).
+ max_prec/0, type_inop_prec/1, type_preop_prec/1]).
-define(PADDING, 2).
-define(PAPER, 80).
@@ -50,7 +50,8 @@
| fun((erl_syntax:syntaxTree(), _, _) -> prettypr:document()).
-type clause_t() :: 'case_expr' | 'cond_expr' | 'fun_expr'
| 'if_expr' | 'receive_expr' | 'try_expr'
- | {'function', prettypr:document()}.
+ | {'function', prettypr:document()}
+ | 'spec'.
-record(ctxt, {prec = 0 :: integer(),
sub_indent = 2 :: non_neg_integer(),
@@ -535,9 +536,6 @@ lay_2(Node, Ctxt) ->
As = seq(erl_syntax:application_arguments(Node),
floating(text(",")), reset_prec(Ctxt),
fun lay/2),
-%% D1 = beside(D, beside(text("("),
-%% beside(par(As),
-%% floating(text(")"))))),
D1 = beside(D, beside(text("("),
beside(par(As),
floating(text(")"))))),
@@ -651,7 +649,7 @@ lay_2(Node, Ctxt) ->
beside(D1, beside(text(":"), D2));
%%
- %% The rest is in alphabetical order
+ %% The rest is in alphabetical order (except map and types)
%%
arity_qualifier ->
@@ -666,18 +664,67 @@ lay_2(Node, Ctxt) ->
%% a period. If the arguments is `none', we only output the
%% attribute name, without following parentheses.
Ctxt1 = reset_prec(Ctxt),
- N = erl_syntax:attribute_name(Node),
- D = case erl_syntax:attribute_arguments(Node) of
- none ->
+ Args = erl_syntax:attribute_arguments(Node),
+ N = erl_syntax:attribute_name(Node),
+ D = case attribute_type(Node) of
+ spec ->
+ [SpecTuple] = Args,
+ [FuncName, FuncTypes] =
+ erl_syntax:tuple_elements(SpecTuple),
+ Name =
+ case erl_syntax:type(FuncName) of
+ tuple ->
+ case erl_syntax:tuple_elements(FuncName) of
+ [F0, _] ->
+ F0;
+ [M0, F0, _] ->
+ erl_syntax:module_qualifier(M0,
+ F0);
+ _ ->
+ FuncName
+ end;
+ _ ->
+ FuncName
+ end,
+ Types = dodge_macros(FuncTypes),
+ D1 = lay_clauses(erl_syntax:concrete(Types),
+ spec, Ctxt1),
+ beside(follow(lay(N, Ctxt1),
+ lay(Name, Ctxt1),
+ Ctxt1#ctxt.break_indent),
+ D1);
+ type ->
+ [TypeTuple] = Args,
+ [Name, Type0, Elements] =
+ erl_syntax:tuple_elements(TypeTuple),
+ TypeName = dodge_macros(Name),
+ Type = dodge_macros(Type0),
+ As0 = dodge_macros(Elements),
+ As = erl_syntax:concrete(As0),
+ D1 = lay_type_application(TypeName, As, Ctxt1),
+ D2 = lay(erl_syntax:concrete(Type), Ctxt1),
+ beside(follow(lay(N, Ctxt1),
+ beside(D1, floating(text(" :: "))),
+ Ctxt1#ctxt.break_indent),
+ D2);
+ Tag when Tag =:= export_type;
+ Tag =:= optional_callbacks ->
+ [FuncNs] = Args,
+ FuncNames = erl_syntax:concrete(dodge_macros(FuncNs)),
+ As = unfold_function_names(FuncNames),
+ beside(lay(N, Ctxt1),
+ beside(text("("),
+ beside(lay(As, Ctxt1),
+ floating(text(")")))));
+ _ when Args =:= none ->
lay(N, Ctxt1);
- Args ->
- As = seq(Args, floating(text(",")), Ctxt1,
- fun lay/2),
+ _ ->
+ D1 = par(seq(Args, floating(text(",")), Ctxt1,
+ fun lay/2)),
beside(lay(N, Ctxt1),
beside(text("("),
- beside(par(As),
- floating(text(")")))))
- end,
+ beside(D1, floating(text(")")))))
+ end,
beside(floating(text("-")), beside(D, floating(text("."))));
binary ->
@@ -928,6 +975,16 @@ lay_2(Node, Ctxt) ->
text ->
text(erl_syntax:text_string(Node));
+ typed_record_field ->
+ {_, Prec, _} = type_inop_prec('::'),
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:typed_record_field_body(Node), Ctxt1),
+ D2 = lay(erl_syntax:typed_record_field_type(Node),
+ set_prec(Ctxt, Prec)),
+ D3 = par([D1, floating(text(" ::")), D2],
+ Ctxt1#ctxt.break_indent),
+ maybe_parentheses(D3, Prec, Ctxt);
+
try_expr ->
Ctxt1 = reset_prec(Ctxt),
D1 = sep(seq(erl_syntax:try_expr_body(Node),
@@ -965,9 +1022,236 @@ lay_2(Node, Ctxt) ->
warning_marker ->
E = erl_syntax:warning_marker_info(Node),
beside(text("%% WARNING: "),
- lay_error_info(E, reset_prec(Ctxt)))
+ lay_error_info(E, reset_prec(Ctxt)));
+
+ %%
+ %% Types
+ %%
+
+ annotated_type ->
+ {_, Prec, _} = type_inop_prec('::'),
+ D1 = lay(erl_syntax:annotated_type_name(Node),
+ reset_prec(Ctxt)),
+ D2 = lay(erl_syntax:annotated_type_body(Node),
+ set_prec(Ctxt, Prec)),
+ D3 = follow(beside(D1, floating(text(" ::"))), D2,
+ Ctxt#ctxt.break_indent),
+ maybe_parentheses(D3, Prec, Ctxt);
+
+ type_application ->
+ Name = erl_syntax:type_application_name(Node),
+ Arguments = erl_syntax:type_application_arguments(Node),
+ %% Prefer shorthand notation.
+ case erl_syntax_lib:analyze_type_application(Node) of
+ {nil, 0} ->
+ text("[]");
+ {list, 1} ->
+ [A] = Arguments,
+ D1 = lay(A, reset_prec(Ctxt)),
+ beside(text("["), beside(D1, text("]")));
+ {nonempty_list, 1} ->
+ [A] = Arguments,
+ D1 = lay(A, reset_prec(Ctxt)),
+ beside(text("["), beside(D1, text(", ...]")));
+ _ ->
+ lay_type_application(Name, Arguments, Ctxt)
+ end;
+
+ bitstring_type ->
+ Ctxt1 = set_prec(Ctxt, max_prec()),
+ M = erl_syntax:bitstring_type_m(Node),
+ N = erl_syntax:bitstring_type_n(Node),
+ D1 = [beside(text("_:"), lay(M, Ctxt1)) ||
+ (erl_syntax:type(M) =/= integer orelse
+ erl_syntax:integer_value(M) =/= 0)],
+ D2 = [beside(text("_:_*"), lay(N, Ctxt1)) ||
+ (erl_syntax:type(N) =/= integer orelse
+ erl_syntax:integer_value(N) =/= 0)],
+ F = fun(D, _) -> D end,
+ D = seq(D1 ++ D2, floating(text(",")), Ctxt1, F),
+ beside(floating(text("<<")),
+ beside(par(D), floating(text(">>"))));
+
+ fun_type ->
+ text("fun()");
+
+ constrained_function_type ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:constrained_function_type_body(Node),
+ Ctxt1),
+ D2 = lay(erl_syntax:constrained_function_type_argument(Node),
+ Ctxt1),
+ beside(D1,
+ beside(floating(text(" when ")), D2));
+
+ function_type ->
+ {Before, After} = case Ctxt#ctxt.clause of
+ spec ->
+ {"", ""};
+ _ ->
+ {"fun(", ")"}
+ end,
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = case erl_syntax:function_type_arguments(Node) of
+ any_arity ->
+ text("(...)");
+ Arguments ->
+ As = seq(Arguments,
+ floating(text(",")), Ctxt1,
+ fun lay/2),
+ beside(text("("),
+ beside(par(As),
+ floating(text(")"))))
+ end,
+ D2 = lay(erl_syntax:function_type_return(Node), Ctxt1),
+ beside(floating(text(Before)),
+ beside(D1,
+ beside(floating(text(" -> ")),
+ beside(D2, floating(text(After))))));
+
+ constraint ->
+ Name = erl_syntax:constraint_argument(Node),
+ Args = erl_syntax:constraint_body(Node),
+ case is_subtype(Name, Args) of
+ true ->
+ [Var, Type] = Args,
+ {PrecL, Prec, PrecR} = type_inop_prec('::'),
+ D1 = lay(Var, set_prec(Ctxt, PrecL)),
+ D2 = lay(Type, set_prec(Ctxt, PrecR)),
+ D3 = follow(beside(D1, floating(text(" ::"))), D2,
+ Ctxt#ctxt.break_indent),
+ maybe_parentheses(D3, Prec, Ctxt);
+ false ->
+ lay_type_application(Name, Args, Ctxt)
+ end;
+
+ map_type ->
+ case erl_syntax:map_type_fields(Node) of
+ any_size ->
+ text("map()");
+ Fs ->
+ {Prec, _PrecR} = type_preop_prec('#'),
+ Es = lay_map_fields(Fs,
+ floating(text(",")),
+ reset_prec(Ctxt)),
+ D = beside(floating(text("#{")),
+ beside(par(Es),
+ floating(text("}")))),
+ maybe_parentheses(D, Prec, Ctxt)
+ end;
+
+ map_type_assoc ->
+ Name = erl_syntax:map_type_assoc_name(Node),
+ Value = erl_syntax:map_type_assoc_value(Node),
+ lay_type_assoc(Name, Value, Ctxt);
+
+ map_type_exact ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:map_type_exact_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:map_type_exact_value(Node), Ctxt1),
+ par([D1, floating(text(":=")), D2], Ctxt1#ctxt.break_indent);
+
+ integer_range_type ->
+ {PrecL, Prec, PrecR} = type_inop_prec('..'),
+ D1 = lay(erl_syntax:integer_range_type_low(Node),
+ set_prec(Ctxt, PrecL)),
+ D2 = lay(erl_syntax:integer_range_type_high(Node),
+ set_prec(Ctxt, PrecR)),
+ D3 = beside(D1, beside(text(".."), D2)),
+ maybe_parentheses(D3, Prec, Ctxt);
+
+ record_type ->
+ {Prec, _PrecR} = type_preop_prec('#'),
+ D1 = beside(text("#"),
+ lay(erl_syntax:record_type_name(Node),
+ reset_prec(Ctxt))),
+ Es = seq(erl_syntax:record_type_fields(Node),
+ floating(text(",")), reset_prec(Ctxt),
+ fun lay/2),
+ D2 = beside(D1,
+ beside(text("{"),
+ beside(par(Es),
+ floating(text("}"))))),
+ maybe_parentheses(D2, Prec, Ctxt);
+
+ record_type_field ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:record_type_field_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:record_type_field_type(Node), Ctxt1),
+ par([D1, floating(text("::")), D2], Ctxt1#ctxt.break_indent);
+
+ tuple_type ->
+ case erl_syntax:tuple_type_elements(Node) of
+ any_size ->
+ text("tuple()");
+ Elements ->
+ Es = seq(Elements,
+ floating(text(",")), reset_prec(Ctxt),
+ fun lay/2),
+ beside(floating(text("{")),
+ beside(par(Es), floating(text("}"))))
+ end;
+
+ type_union ->
+ {_, Prec, PrecR} = type_inop_prec('|'),
+ Es = par(seq(erl_syntax:type_union_types(Node),
+ floating(text(" |")), set_prec(Ctxt, PrecR),
+ fun lay/2)),
+ maybe_parentheses(Es, Prec, Ctxt);
+
+ user_type_application ->
+ lay_type_application(erl_syntax:user_type_application_name(Node),
+ erl_syntax:user_type_application_arguments(Node),
+ Ctxt)
+
+ end.
+
+attribute_type(Node) ->
+ N = erl_syntax:attribute_name(Node),
+ case catch erl_syntax:concrete(N) of
+ opaque ->
+ type;
+ spec ->
+ spec;
+ callback ->
+ spec;
+ type ->
+ type;
+ export_type ->
+ export_type;
+ optional_callbacks ->
+ optional_callbacks;
+ _ ->
+ N
end.
+is_subtype(Name, [Var, _]) ->
+ (erl_syntax:is_atom(Name, is_subtype) andalso
+ erl_syntax:type(Var) =:= variable);
+is_subtype(_, _) -> false.
+
+unfold_function_names(Ns) ->
+ F = fun ({Atom, Arity}) ->
+ erl_syntax:arity_qualifier(erl_syntax:atom(Atom),
+ erl_syntax:integer(Arity))
+ end,
+ erl_syntax:list([F(N) || N <- Ns]).
+
+%% Macros are not handled well.
+dodge_macros(Type) ->
+ F = fun (T) ->
+ case erl_syntax:type(T) of
+ macro ->
+ Var = erl_syntax:macro_name(T),
+ VarName0 = erl_syntax:variable_name(Var),
+ VarName = list_to_atom("?"++atom_to_list(VarName0)),
+ Atom = erl_syntax:atom(VarName),
+ Atom;
+ _ -> T
+ end
+ end,
+ erl_syntax_lib:map(F, Type).
+
lay_parentheses(D, _Ctxt) ->
beside(floating(text("(")), beside(D, floating(text(")")))).
@@ -1020,6 +1304,8 @@ split_string_1([], _N, _L, As) ->
split_string_2([$^, X | Xs], N, L, As) ->
split_string_1(Xs, N - 2, L - 2, [X, $^ | As]);
+split_string_2([$x, ${ | Xs], N, L, As) ->
+ split_string_3(Xs, N - 2, L - 2, [${, $x | As]);
split_string_2([X1, X2, X3 | Xs], N, L, As) when
X1 >= $0, X1 =< $7, X2 >= $0, X2 =< $7, X3 >= $0, X3 =< $7 ->
split_string_1(Xs, N - 3, L - 3, [X3, X2, X1 | As]);
@@ -1029,6 +1315,15 @@ split_string_2([X1, X2 | Xs], N, L, As) when
split_string_2([X | Xs], N, L, As) ->
split_string_1(Xs, N - 1, L - 1, [X | As]).
+split_string_3([$} | Xs], N, L, As) ->
+ split_string_1(Xs, N - 1, L - 1, [$} | As]);
+split_string_3([X | Xs], N, L, As) when
+ X >= $0, X =< $9; X >= $a, X =< $z; X >= $A, X =< $Z ->
+ split_string_3(Xs, N - 1, L -1, [X | As]);
+split_string_3([X | Xs], N, L, As) when
+ X >= $0, X =< $9 ->
+ split_string_1(Xs, N - 1, L -1, [X | As]).
+
%% Note that there is nothing in `lay_clauses' that actually requires
%% that the elements have type `clause'; it just sets up the proper
%% context and arranges the elements suitably for clauses.
@@ -1105,6 +1400,53 @@ lay_error_info(T, Ctxt) ->
lay_concrete(T, Ctxt) ->
lay(erl_syntax:abstract(T), Ctxt).
+lay_map_fields([H | T], Separator, Ctxt) ->
+ case T of
+ [] ->
+ [case erl_syntax:type(H) of
+ map_type_assoc ->
+ lay_last_type_assoc(H, Ctxt);
+ _ ->
+ lay(H, Ctxt)
+ end];
+ _ ->
+ [maybe_append(Separator, lay(H, Ctxt))
+ | lay_map_fields(T, Separator, Ctxt)]
+ end;
+lay_map_fields([], _, _) ->
+ [empty()].
+
+lay_last_type_assoc(Node, Ctxt) ->
+ Name = erl_syntax:map_type_assoc_name(Node),
+ Value = erl_syntax:map_type_assoc_value(Node),
+ IsAny = fun({type,_,any,[]}) -> true;
+ %% ({var,_,'_'}) -> true;
+ (_) -> false
+ end,
+ case IsAny(Name) andalso IsAny(Value) of
+ true ->
+ text("...");
+ false ->
+ lay_type_assoc(Name, Value, Ctxt)
+ end.
+
+lay_type_assoc(Name, Value, Ctxt) ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(Name, Ctxt1),
+ D2 = lay(Value, Ctxt1),
+ par([D1, floating(text("=>")), D2], Ctxt1#ctxt.break_indent).
+
+lay_type_application(Name, Arguments, Ctxt) ->
+ {PrecL, Prec} = func_prec(), %
+ D1 = lay(Name, set_prec(Ctxt, PrecL)),
+ As = seq(Arguments,
+ floating(text(",")), reset_prec(Ctxt),
+ fun lay/2),
+ D = beside(D1, beside(text("("),
+ beside(par(As),
+ floating(text(")"))))),
+ maybe_parentheses(D, Prec, Ctxt).
+
seq([H | T], Separator, Ctxt, Fun) ->
case T of
[] ->
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 97b5797b06..f4cda814fc 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -120,6 +120,9 @@
normalize_list/1,
compact_list/1,
+ annotated_type/2,
+ annotated_type_name/1,
+ annotated_type_body/1,
application/2,
application/3,
application_arguments/1,
@@ -150,6 +153,9 @@
binary_generator/2,
binary_generator_body/1,
binary_generator_pattern/1,
+ bitstring_type/2,
+ bitstring_type_m/1,
+ bitstring_type_n/1,
block_expr/1,
block_expr_body/1,
case_expr/2,
@@ -175,6 +181,12 @@
cond_expr_clauses/1,
conjunction/1,
conjunction_body/1,
+ constrained_function_type/2,
+ constrained_function_type_body/1,
+ constrained_function_type_argument/1,
+ constraint/2,
+ constraint_argument/1,
+ constraint_body/1,
disjunction/1,
disjunction_body/1,
eof_marker/0,
@@ -188,10 +200,15 @@
fun_expr/1,
fun_expr_arity/1,
fun_expr_clauses/1,
+ fun_type/0,
function/2,
function_arity/1,
function_clauses/1,
function_name/1,
+ function_type/1,
+ function_type/2,
+ function_type_arguments/1,
+ function_type_return/1,
generator/2,
generator_body/1,
generator_pattern/1,
@@ -209,6 +226,9 @@
is_integer/2,
integer_value/1,
integer_literal/1,
+ integer_range_type/2,
+ integer_range_type_low/1,
+ integer_range_type_high/1,
list/1,
list/2,
list_comp/2,
@@ -230,6 +250,15 @@
map_field_exact/2,
map_field_exact_name/1,
map_field_exact_value/1,
+ map_type/0,
+ map_type/1,
+ map_type_fields/1,
+ map_type_assoc/2,
+ map_type_assoc_name/1,
+ map_type_assoc_value/1,
+ map_type_exact/2,
+ map_type_exact_name/1,
+ map_type_exact_value/1,
match_expr/2,
match_expr_body/1,
match_expr_pattern/1,
@@ -270,6 +299,12 @@
record_index_expr/2,
record_index_expr_field/1,
record_index_expr_type/1,
+ record_type/2,
+ record_type_name/1,
+ record_type_fields/1,
+ record_type_field/2,
+ record_type_field_name/1,
+ record_type_field_type/1,
size_qualifier/2,
size_qualifier_argument/1,
size_qualifier_body/1,
@@ -288,6 +323,18 @@
try_expr_clauses/1,
try_expr_handlers/1,
try_expr_after/1,
+ tuple_type/0,
+ tuple_type/1,
+ tuple_type_elements/1,
+ type_application/2,
+ type_application/3,
+ type_application_name/1,
+ type_application_arguments/1,
+ type_union/1,
+ type_union_types/1,
+ typed_record_field/2,
+ typed_record_field_body/1,
+ typed_record_field_type/1,
class_qualifier/2,
class_qualifier_argument/1,
class_qualifier_body/1,
@@ -295,6 +342,9 @@
tuple_elements/1,
tuple_size/1,
underscore/0,
+ user_type_application/2,
+ user_type_application_name/1,
+ user_type_application_arguments/1,
variable/1,
variable_name/1,
variable_literal/1,
@@ -412,23 +462,28 @@
%% <center><table border="1">
%% <tr>
%% <td>application</td>
+%% <td>annotated_type</td>
%% <td>arity_qualifier</td>
%% <td>atom</td>
-%% <td>attribute</td>
%% </tr><tr>
+%% <td>attribute</td>
%% <td>binary</td>
%% <td>binary_field</td>
+%% <td>bitstring_type</td>
+%% </tr><tr>
%% <td>block_expr</td>
%% <td>case_expr</td>
-%% </tr><tr>
%% <td>catch_expr</td>
%% <td>char</td>
+%% </tr><tr>
%% <td>class_qualifier</td>
%% <td>clause</td>
-%% </tr><tr>
%% <td>comment</td>
%% <td>cond_expr</td>
+%% </tr><tr>
%% <td>conjunction</td>
+%% <td>constrained_function_type</td>
+%% <td>constraint</td>
%% <td>disjunction</td>
%% </tr><tr>
%% <td>eof_marker</td>
@@ -437,43 +492,58 @@
%% <td>form_list</td>
%% </tr><tr>
%% <td>fun_expr</td>
+%% <td>fun_type</td>
%% <td>function</td>
+%% <td>function_type</td>
+%% </tr><tr>
%% <td>generator</td>
%% <td>if_expr</td>
-%% </tr><tr>
%% <td>implicit_fun</td>
%% <td>infix_expr</td>
+%% </tr><tr>
%% <td>integer</td>
+%% <td>integer_range_type</td>
%% <td>list</td>
-%% </tr><tr>
%% <td>list_comp</td>
+%% </tr><tr>
%% <td>macro</td>
%% <td>map_expr</td>
%% <td>map_field_assoc</td>
-%% </tr><tr>
%% <td>map_field_exact</td>
+%% </tr><tr>
+%% <td>map_type</td>
+%% <td>map_type_assoc</td>
+%% <td>map_type_exact</td>
%% <td>match_expr</td>
%% <td>module_qualifier</td>
-%% <td>named_fun_expr</td>
%% </tr><tr>
+%% <td>named_fun_expr</td>
%% <td>nil</td>
%% <td>operator</td>
%% <td>parentheses</td>
-%% <td>prefix_expr</td>
%% </tr><tr>
+%% <td>prefix_expr</td>
%% <td>receive_expr</td>
%% <td>record_access</td>
%% <td>record_expr</td>
-%% <td>record_field</td>
%% </tr><tr>
+%% <td>record_field</td>
%% <td>record_index_expr</td>
+%% <td>record_type</td>
+%% <td>record_type_field</td>
+%% </tr><tr>
%% <td>size_qualifier</td>
%% <td>string</td>
%% <td>text</td>
-%% </tr><tr>
%% <td>try_expr</td>
+%% </tr><tr>
%% <td>tuple</td>
+%% <td>tuple_type</td>
+%% <td>typed_record_field</td>
+%% <td>type_application</td>
+%% <td>type_union</td>
%% <td>underscore</td>
+%% <td>user_type_application</td>
%% <td>variable</td>
%% </tr><tr>
%% <td>warning_marker</td>
@@ -487,12 +557,14 @@
%% always have the same name as the node type itself.
%%
%% @see tree/2
+%% @see annotated_type/2
%% @see application/3
%% @see arity_qualifier/2
%% @see atom/1
%% @see attribute/2
%% @see binary/1
%% @see binary_field/2
+%% @see bitstring_type/2
%% @see block_expr/1
%% @see case_expr/2
%% @see catch_expr/1
@@ -502,24 +574,34 @@
%% @see comment/2
%% @see cond_expr/1
%% @see conjunction/1
+%% @see constrained_function_type/2
+%% @see constraint/2
%% @see disjunction/1
%% @see eof_marker/0
%% @see error_marker/1
%% @see float/1
%% @see form_list/1
%% @see fun_expr/1
+%% @see fun_type/0
%% @see function/2
+%% @see function_type/1
+%% @see function_type/2
%% @see generator/2
%% @see if_expr/1
%% @see implicit_fun/2
%% @see infix_expr/3
%% @see integer/1
+%% @see integer_range_type/2
%% @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 map_type/0
+%% @see map_type/1
+%% @see map_type_assoc/2
+%% @see map_type_exact/2
%% @see match_expr/2
%% @see module_qualifier/2
%% @see named_fun_expr/2
@@ -532,12 +614,20 @@
%% @see record_expr/2
%% @see record_field/2
%% @see record_index_expr/2
+%% @see record_type/2
+%% @see record_type_field/2
%% @see size_qualifier/2
%% @see string/1
%% @see text/1
%% @see try_expr/3
%% @see tuple/1
+%% @see tuple_type/0
+%% @see tuple_type/1
+%% @see typed_record_field/2
+%% @see type_application/2
+%% @see type_union/1
%% @see underscore/0
+%% @see user_type_application/2
%% @see variable/1
%% @see warning_marker/1
@@ -602,6 +692,25 @@ type(Node) ->
{remote, _, _, _} -> module_qualifier;
{'try', _, _, _, _, _} -> try_expr;
{tuple, _, _} -> tuple;
+
+ %% Type types
+ {ann_type, _, _} -> annotated_type;
+ {remote_type, _, _} -> type_application;
+ {type, _, binary, [_, _]} -> bitstring_type;
+ {type, _, bounded_fun, [_, _]} -> constrained_function_type;
+ {type, _, constraint, [_, _]} -> constraint;
+ {type, _, 'fun', []} -> fun_type;
+ {type, _, 'fun', [_, _]} -> function_type;
+ {type, _, map, _} -> map_type;
+ {type, _, map_field_assoc, _} -> map_type_assoc;
+ {type, _, map_field_exact, _} -> map_type_exact;
+ {type, _, record, _} -> record_type;
+ {type, _, field_type, _} -> record_type_field;
+ {type, _, range, _} -> integer_range_type;
+ {type, _, tuple, _} -> tuple_type;
+ {type, _, union, _} -> type_union;
+ {type, _, _, _} -> type_application;
+ {user_type, _, _, _} -> user_type_application;
_ ->
erlang:error({badarg, Node})
end.
@@ -621,6 +730,7 @@ type(Node) ->
%% <td>`error_marker'</td>
%% </tr><tr>
%% <td>`float'</td>
+%% <td>`fun_type'</td>
%% <td>`integer'</td>
%% <td>`nil'</td>
%% <td>`operator'</td>
@@ -633,7 +743,13 @@ type(Node) ->
%% </tr>
%% </table></center>
%%
+%% A node of type `map_expr' is a leaf node if and only if it has no
+%% argument and no fields.
+%% A node of type `map_type' is a leaf node if and only if it has no
+%% fields (`any_size').
%% A node of type `tuple' is a leaf node if and only if its arity is zero.
+%% A node of type `tuple_type' is a leaf node if and only if it has no
+%% elements (`any_size').
%%
%% Note: not all literals are leaf nodes, and vice versa. E.g.,
%% tuples with nonzero arity and nonempty lists may be literals, but are
@@ -653,6 +769,7 @@ is_leaf(Node) ->
eof_marker -> true;
error_marker -> true;
float -> true;
+ fun_type -> true;
integer -> true;
nil -> true;
operator -> true; % nonstandard type
@@ -661,7 +778,9 @@ is_leaf(Node) ->
map_expr ->
map_expr_fields(Node) =:= [] andalso
map_expr_argument(Node) =:= none;
+ map_type -> map_type_fields(Node) =:= any_size;
tuple -> tuple_elements(Node) =:= [];
+ tuple_type -> tuple_type_elements(Node) =:= any_size;
underscore -> true;
variable -> true;
warning_marker -> true;
@@ -3114,6 +3233,39 @@ attribute(Name) ->
%% `Imports' is `{Module, [{A1, N1}, ..., {Ak, Nk}]}', or
%% `-import(A1.....An).', if `Imports' is `[A1, ..., An]'.
%%
+%% {attribute, Pos, export_type, ExportedTypes}
+%%
+%% ExportedTypes = [{atom(), integer()}]
+%%
+%% Representing `-export_type([N1/A1, ..., Nk/Ak]).',
+%% if `ExportedTypes' is `[{N1, A1}, ..., {Nk, Ak}]'.
+%%
+%% {attribute, Pos, optional_callbacks, OptionalCallbacks}
+%%
+%% OptionalCallbacks = [{atom(), integer()}]
+%%
+%% Representing `-optional_callbacks([A1/N1, ..., Ak/Nk]).',
+%% if `OptionalCallbacks' is `[{A1, N1}, ..., {Ak, Nk}]'.
+%%
+%% {attribute, Pos, SpecTag, {FuncSpec, FuncType}}
+%%
+%% SpecTag = spec | callback
+%% FuncSpec = {module(), atom(), arity()} | {atom(), arity()}
+%% FuncType = a (possibly constrained) function type
+%%
+%% Representing `-SpecTag M:F/A Ft1; ...; Ftk.' or
+%% `-SpecTag F/A Ft1; ...; Ftk.', if `FuncTypes' is
+%% `[Ft1, ..., Ftk]'.
+%%
+%% {attribute, Pos, TypeTag, {Name, Type, Parameters}}
+%%
+%% TypeTag = type | opaque
+%% Type = a type
+%% Parameters = [Variable]
+%%
+%% Representing `-TypeTag Name(V1, ..., Vk) :: Type .'
+%% if `Parameters' is `[V1, ..., Vk]'.
+%%
%% {attribute, Pos, file, Position}
%%
%% Position = {filename(), integer()}
@@ -3125,13 +3277,19 @@ attribute(Name) ->
%%
%% Info = {Name, [Entries]}
%% Name = atom()
-%% Entries = {record_field, Pos, atom()}
-%% | {record_field, Pos, atom(), erl_parse()}
%%
-%% Representing `-record(Name, {<F1>, ..., <Fn>}).', if `Info' is
+%% Entries = UntypedEntries
+%% | {typed_record_field, UntypedEntries, Type}
+%% UntypedEntries = {record_field, Pos, atom()}
+%% | {record_field, Pos, atom(), erl_parse()}
+%%
+%% Representing `-record(Name, {<F1>, ..., <Fn>}).', if `Info' is
%% `{Name, [D1, ..., D1]}', where each `Fi' is either `Ai = <Ei>',
%% if the corresponding `Di' is `{record_field, Pos, Ai, Ei}', or
-%% otherwise simply `Ai', if `Di' is `{record_field, Pos, Ai}'.
+%% otherwise simply `Ai', if `Di' is `{record_field, Pos, Ai}', or
+%% `Ai = <Ei> :: <Ti>', if `Di' is `{typed_record_field,
+%% {record_field, Pos, Ai, Ei}, Ti}', or `Ai :: <Ti>', if `Di' is
+%% `{typed_record_field, {record_field, Pos, Ai}, Ti}'.
%%
%% {attribute, L, Name, Term}
%%
@@ -3309,11 +3467,6 @@ attribute_arguments(Node) ->
[set_pos(
list(unfold_function_names(Data, Pos)),
Pos)];
- optional_callbacks ->
- D = try list(unfold_function_names(Data, Pos))
- catch _:_ -> abstract(Data)
- end,
- [set_pos(D, Pos)];
import ->
{Module, Imports} = Data,
[set_pos(atom(Module), Pos),
@@ -4183,7 +4336,8 @@ record_field(Name) ->
%% type(Node) = record_field
%% data(Node) = #record_field{name :: Name, value :: Value}
%%
-%% Name = Value = syntaxTree()
+%% Name = syntaxTree()
+%% Value = none | syntaxTree()
-spec record_field(syntaxTree(), 'none' | syntaxTree()) -> syntaxTree().
@@ -4568,7 +4722,7 @@ application(Module, Name, Arguments) ->
%%
%% `erl_parse' representation:
%%
-%% {call, Pos, Fun, Args}
+%% {call, Pos, Operator, Args}
%%
%% Operator = erl_parse()
%% Arguments = [erl_parse()]
@@ -4623,6 +4777,1095 @@ application_arguments(Node) ->
(data(Node1))#application.arguments
end.
+%% =====================================================================
+%% @doc Creates an abstract annotated type expression. The result
+%% represents "<code><em>Name</em> :: <em>Type</em></code>".
+%%
+%% @see annotated_type_name/1
+%% @see annotated_type_body/1
+
+-record(annotated_type, {name :: syntaxTree(), body :: syntaxTree()}).
+
+%% type(Node) = annotated_type
+%% data(Node) = #annotated_type{name :: Name,
+%% body :: Type}
+%%
+%% Name = syntaxTree()
+%% Type = syntaxTree()
+%%
+%% `erl_parse' representation:
+%%
+%% {ann_type, Pos, [Name, Type]}
+%%
+%% Name = erl_parse()
+%% Type = erl_parse()
+
+-spec annotated_type(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+annotated_type(Name, Type) ->
+ tree(annotated_type, #annotated_type{name = Name, body = Type}).
+
+revert_annotated_type(Node) ->
+ Pos = get_pos(Node),
+ Name = annotated_type_name(Node),
+ Type = annotated_type_body(Node),
+ {ann_type, Pos, [Name, Type]}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of an `annotated_type' node.
+%%
+%% @see annotated_type/2
+
+-spec annotated_type_name(syntaxTree()) -> syntaxTree().
+
+annotated_type_name(Node) ->
+ case unwrap(Node) of
+ {ann_type, _, [Name, _]} ->
+ Name;
+ Node1 ->
+ (data(Node1))#annotated_type.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the type subtrees of an `annotated_type' node.
+%%
+%% @see annotated_type/2
+
+-spec annotated_type_body(syntaxTree()) -> syntaxTree().
+
+annotated_type_body(Node) ->
+ case unwrap(Node) of
+ {ann_type, _, [_, Type]} ->
+ Type;
+ Node1 ->
+ (data(Node1))#annotated_type.body
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract fun of any type. The result represents
+%% "<code>fun()</code>".
+
+%% type(Node) = fun_type
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, 'fun', []}
+
+-spec fun_type() -> syntaxTree().
+
+fun_type() ->
+ tree(fun_type).
+
+revert_fun_type(Node) ->
+ Pos = get_pos(Node),
+ {type, Pos, 'fun', []}.
+
+
+%% =====================================================================
+%% @doc Creates an abstract type application expression. If
+%% `Module' is `none', this is call is equivalent
+%% to `type_application(TypeName, Arguments)', otherwise it is
+%% equivalent to `type_application(module_qualifier(Module, TypeName),
+%% Arguments)'.
+%%
+%% (This is a utility function.)
+%%
+%% @see type_application/2
+%% @see module_qualifier/2
+
+-spec type_application('none' | syntaxTree(), syntaxTree(), [syntaxTree()]) ->
+ syntaxTree().
+
+type_application(none, TypeName, Arguments) ->
+ type_application(TypeName, Arguments);
+type_application(Module, TypeName, Arguments) ->
+ type_application(module_qualifier(Module, TypeName), Arguments).
+
+
+%% =====================================================================
+%% @doc Creates an abstract type application expression. If `Arguments' is
+%% `[T1, ..., Tn]', the result represents
+%% "<code><em>TypeName</em>(<em>T1</em>, ...<em>Tn</em>)</code>".
+%%
+%% @see user_type_application/2
+%% @see type_application/3
+%% @see type_application_name/1
+%% @see type_application_arguments/1
+
+-record(type_application, {type_name :: syntaxTree(),
+ arguments :: [syntaxTree()]}).
+
+%% type(Node) = type_application
+%% data(Node) = #type_application{type_name :: TypeName,
+%% arguments :: Arguments}
+%%
+%% TypeName = syntaxTree()
+%% Arguments = [syntaxTree()]
+%%
+%% `erl_parse' representation:
+%%
+%% {remote, Pos, [Module, Name, Arguments]} |
+%% {type, Pos, Name, Arguments}
+%%
+%% Module = erl_parse()
+%% Name = atom()
+%% Arguments = [erl_parse()]
+
+-spec type_application(syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+type_application(TypeName, Arguments) ->
+ tree(type_application,
+ #type_application{type_name = TypeName, arguments = Arguments}).
+
+revert_type_application(Node) ->
+ Pos = get_pos(Node),
+ TypeName = type_application_name(Node),
+ Arguments = type_application_arguments(Node),
+ case type(TypeName) of
+ module_qualifier ->
+ Module = module_qualifier_argument(TypeName),
+ Name = module_qualifier_body(TypeName),
+ {remote_type, Pos, [Module, Name, Arguments]};
+ atom ->
+ {type, Pos, atom_value(TypeName), Arguments}
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the type name subtree of a `type_application' node.
+%%
+%% @see type_application/2
+
+-spec type_application_name(syntaxTree()) -> syntaxTree().
+
+type_application_name(Node) ->
+ case unwrap(Node) of
+ {remote_type, _, [Module, Name, _]} ->
+ module_qualifier(Module, Name);
+ {type, Pos, Name, _} ->
+ set_pos(atom(Name), Pos);
+ Node1 ->
+ (data(Node1))#type_application.type_name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the arguments subtrees of a `type_application' node.
+%%
+%% @see type_application/2
+
+-spec type_application_arguments(syntaxTree()) -> [syntaxTree()].
+
+type_application_arguments(Node) ->
+ case unwrap(Node) of
+ {remote_type, _, [_, _, Arguments]} ->
+ Arguments;
+ {type, _, _, Arguments} ->
+ Arguments;
+ Node1 ->
+ (data(Node1))#type_application.arguments
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract bitstring type. The result represents
+%% "<code><em>&lt;&lt;_:M, _:_*N&gt;&gt;</em></code>".
+%%
+%% @see bitstring_type_m/1
+%% @see bitstring_type_n/1
+
+-record(bitstring_type, {m :: syntaxTree(), n :: syntaxTree()}).
+
+%% type(Node) = bitstring_type
+%% data(Node) = #bitstring_type{m :: M, n :: N}
+%%
+%% M = syntaxTree()
+%% N = syntaxTree()
+%%
+
+-spec bitstring_type(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+bitstring_type(M, N) ->
+ tree(bitstring_type, #bitstring_type{m = M, n =N}).
+
+revert_bitstring_type(Node) ->
+ Pos = get_pos(Node),
+ M = bitstring_type_m(Node),
+ N = bitstring_type_n(Node),
+ {type, Pos, binary, [M, N]}.
+
+%% =====================================================================
+%% @doc Returns the number of start bits, `M', of a `bitstring_type' node.
+%%
+%% @see bitstring_type/2
+
+-spec bitstring_type_m(syntaxTree()) -> syntaxTree().
+
+bitstring_type_m(Node) ->
+ case unwrap(Node) of
+ {type, _, binary, [M, _]} ->
+ M;
+ Node1 ->
+ (data(Node1))#bitstring_type.m
+ end.
+
+%% =====================================================================
+%% @doc Returns the segment size, `N', of a `bitstring_type' node.
+%%
+%% @see bitstring_type/2
+
+-spec bitstring_type_n(syntaxTree()) -> syntaxTree().
+
+bitstring_type_n(Node) ->
+ case unwrap(Node) of
+ {type, _, binary, [_, N]} ->
+ N;
+ Node1 ->
+ (data(Node1))#bitstring_type.n
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract constrained function type.
+%% If `FunctionConstraint' is `[C1, ..., Cn]', the result represents
+%% "<code><em>FunctionType</em> when <em>C1</em>, ...<em>Cn</em></code>".
+%%
+%% @see constrained_function_type_body/1
+%% @see constrained_function_type_argument/1
+
+-record(constrained_function_type, {body :: syntaxTree(),
+ argument :: syntaxTree()}).
+
+%% type(Node) = constrained_function_type
+%% data(Node) = #constrained_function_type{body :: FunctionType,
+%% argument :: FunctionConstraint}
+%%
+%% FunctionType = syntaxTree()
+%% FunctionConstraint = syntaxTree()
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, bounded_fun, [FunctionType, FunctionConstraint]}
+%%
+%% FunctionType = erl_parse()
+%% FunctionConstraint = [erl_parse()]
+
+-spec constrained_function_type(syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+constrained_function_type(FunctionType, FunctionConstraint) ->
+ Conj = conjunction(FunctionConstraint),
+ tree(constrained_function_type,
+ #constrained_function_type{body = FunctionType,
+ argument = Conj}).
+
+revert_constrained_function_type(Node) ->
+ Pos = get_pos(Node),
+ FunctionType = constrained_function_type_body(Node),
+ FunctionConstraint =
+ conjunction_body(constrained_function_type_argument(Node)),
+ {type, Pos, bounded_fun, [FunctionType, FunctionConstraint]}.
+
+
+%% =====================================================================
+%% @doc Returns the function type subtree of a
+%% `constrained_function_type' node.
+%%
+%% @see constrained_function_type/2
+
+-spec constrained_function_type_body(syntaxTree()) -> syntaxTree().
+
+constrained_function_type_body(Node) ->
+ case unwrap(Node) of
+ {type, _, bounded_fun, [FunctionType, _]} ->
+ FunctionType;
+ Node1 ->
+ (data(Node1))#constrained_function_type.body
+ end.
+
+%% =====================================================================
+%% @doc Returns the function constraint subtree of a
+%% `constrained_function_type' node.
+%%
+%% @see constrained_function_type/2
+
+-spec constrained_function_type_argument(syntaxTree()) -> syntaxTree().
+
+constrained_function_type_argument(Node) ->
+ case unwrap(Node) of
+ {type, _, bounded_fun, [_, FunctionConstraint]} ->
+ conjunction(FunctionConstraint);
+ Node1 ->
+ (data(Node1))#constrained_function_type.argument
+ end.
+
+
+%% =====================================================================
+%% @equiv function_type(any_arity, Type)
+
+function_type(Type) ->
+ function_type(any_arity, Type).
+
+%% =====================================================================
+%% @doc Creates an abstract function type. If `Arguments' is
+%% `[T1, ..., Tn]', then if it occurs within a function
+%% specification, the result represents
+%% "<code>(<em>T1</em>, ...<em>Tn</em>) -> <em>Return</em></code>"; otherwise
+%% it represents
+%% "<code>fun((<em>T1</em>, ...<em>Tn</em>) -> <em>Return</em>)</code>".
+%% If `Arguments' is `any_arity', it represents
+%% "<code>fun((...) -> <em>Return</em>)</code>".
+%%
+%% Note that the `erl_parse' representation is identical for
+%% "<code><em>FunctionType</em></code>" and
+%% "<code>fun(<em>FunctionType</em>)</code>".
+%%
+%% @see function_type_arguments/1
+%% @see function_type_return/1
+
+-record(function_type, {arguments :: any_arity | [syntaxTree()],
+ return :: syntaxTree()}).
+
+%% type(Node) = function_type
+%% data(Node) = #function_type{arguments :: any | Arguments,
+%% return :: Type}
+%%
+%% Arguments = [syntaxTree()]
+%% Type = syntaxTree()
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, 'fun', [{type, Pos, product, Arguments}, Type]}
+%% {type, Pos, 'fun', [{type, Pos, any}, Type]}
+%%
+%% Arguments = [erl_parse()]
+%% Type = erl_parse()
+
+-spec function_type('any_arity' | syntaxTree(), syntaxTree()) -> syntaxTree().
+
+function_type(Arguments, Return) ->
+ tree(function_type,
+ #function_type{arguments = Arguments, return = Return}).
+
+revert_function_type(Node) ->
+ Pos = get_pos(Node),
+ Type = function_type_return(Node),
+ case function_type_arguments(Node) of
+ any_arity ->
+ {type, Pos, 'fun', [{type, Pos, any}, Type]};
+ Arguments ->
+ {type, Pos, 'fun', [{type, Pos, product, Arguments}, Type]}
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the argument types subtrees of a `function_type' node.
+%% If `Node' represents "<code>fun((...) -> <em>Return</em>)</code>",
+%% `any_arity' is returned; otherwise, if `Node' represents
+%% "<code>(<em>T1</em>, ...<em>Tn</em>) -> <em>Return</em></code>" or
+%% "<code>fun((<em>T1</em>, ...<em>Tn</em>) -> <em>Return</em>)</code>",
+%% `[T1, ..., Tn]' is returned.
+
+%%
+%% @see function_type/1
+%% @see function_type/2
+
+-spec function_type_arguments(syntaxTree()) -> any_arity | [syntaxTree()].
+
+function_type_arguments(Node) ->
+ case unwrap(Node) of
+ {type, _, 'fun', [{type, _, any}, _]} ->
+ any_arity;
+ {type, _, 'fun', [{type, _, product, Arguments}, _]} ->
+ Arguments;
+ Node1 ->
+ (data(Node1))#function_type.arguments
+ end.
+
+%% =====================================================================
+%% @doc Returns the return type subtrees of a `function_type' node.
+%%
+%% @see function_type/1
+%% @see function_type/2
+
+-spec function_type_return(syntaxTree()) -> syntaxTree().
+
+function_type_return(Node) ->
+ case unwrap(Node) of
+ {type, _, 'fun', [_, Type]} ->
+ Type;
+ Node1 ->
+ (data(Node1))#function_type.return
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract (subtype) constraint. The result represents
+%% "<code><em>Name</em> :: <em>Type</em></code>".
+%%
+%% @see constraint_argument/1
+%% @see constraint_body/1
+
+-record(constraint, {name :: syntaxTree(),
+ types :: [syntaxTree()]}).
+
+%% type(Node) = constraint
+%% data(Node) = #constraint{name :: Name,
+%% types :: [Type]}
+%%
+%% Name = syntaxTree()
+%% Type = syntaxTree()
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, constraint, [Name, [Var, Type]]}
+%%
+%% Name = {atom, Pos, is_subtype}
+%% Var = erl_parse()
+%% Type = erl_parse()
+
+-spec constraint(syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+constraint(Name, Types) ->
+ tree(constraint,
+ #constraint{name = Name, types = Types}).
+
+revert_constraint(Node) ->
+ Pos = get_pos(Node),
+ Name = constraint_argument(Node),
+ Types = constraint_body(Node),
+ {type, Pos, constraint, [Name, Types]}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `constraint' node.
+%%
+%% @see constraint/2
+
+-spec constraint_argument(syntaxTree()) -> syntaxTree().
+
+constraint_argument(Node) ->
+ case unwrap(Node) of
+ {type, _, constraint, [Name, _]} ->
+ Name;
+ Node1 ->
+ (data(Node1))#constraint.name
+ end.
+
+%% =====================================================================
+%% @doc Returns the type subtree of a `constraint' node.
+%%
+%% @see constraint/2
+
+-spec constraint_body(syntaxTree()) -> [syntaxTree()].
+
+constraint_body(Node) ->
+ case unwrap(Node) of
+ {type, _, constraint, [_, Types]} ->
+ Types;
+ Node1 ->
+ (data(Node1))#constraint.types
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract map type assoc field. The result represents
+%% "<code><em>Name</em> => <em>Value</em></code>".
+%%
+%% @see map_type_assoc_name/1
+%% @see map_type_assoc_value/1
+%% @see map_type/1
+
+-record(map_type_assoc, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {type, Pos, map_field_assoc, [Name, Value]}
+
+-spec map_type_assoc(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_type_assoc(Name, Value) ->
+ tree(map_type_assoc, #map_type_assoc{name = Name, value = Value}).
+
+revert_map_type_assoc(Node) ->
+ Pos = get_pos(Node),
+ Name = map_type_assoc_name(Node),
+ Value = map_type_assoc_value(Node),
+ {type, Pos, map_type_assoc, [Name, Value]}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_type_assoc' node.
+%%
+%% @see map_type_assoc/2
+
+-spec map_type_assoc_name(syntaxTree()) -> syntaxTree().
+
+map_type_assoc_name(Node) ->
+ case Node of
+ {type, _, map_field_assoc, [Name, _]} ->
+ Name;
+ _ ->
+ (data(Node))#map_type_assoc.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_type_assoc' node.
+%%
+%% @see map_type_assoc/2
+
+-spec map_type_assoc_value(syntaxTree()) -> syntaxTree().
+
+map_type_assoc_value(Node) ->
+ case Node of
+ {type, _, map_field_assoc, [_, Value]} ->
+ Value;
+ _ ->
+ (data(Node))#map_type_assoc.value
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract map type exact field. The result represents
+%% "<code><em>Name</em> := <em>Value</em></code>".
+%%
+%% @see map_type_exact_name/1
+%% @see map_type_exact_value/1
+%% @see map_type/1
+
+-record(map_type_exact, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {type, Pos, map_field_exact, [Name, Value]}
+
+-spec map_type_exact(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_type_exact(Name, Value) ->
+ tree(map_type_exact, #map_type_exact{name = Name, value = Value}).
+
+revert_map_type_exact(Node) ->
+ Pos = get_pos(Node),
+ Name = map_type_exact_name(Node),
+ Value = map_type_exact_value(Node),
+ {type, Pos, map_type_exact, [Name, Value]}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_type_exact' node.
+%%
+%% @see map_type_exact/2
+
+-spec map_type_exact_name(syntaxTree()) -> syntaxTree().
+
+map_type_exact_name(Node) ->
+ case Node of
+ {type, _, map_field_exact, [Name, _]} ->
+ Name;
+ _ ->
+ (data(Node))#map_type_exact.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_type_exact' node.
+%%
+%% @see map_type_exact/2
+
+-spec map_type_exact_value(syntaxTree()) -> syntaxTree().
+
+map_type_exact_value(Node) ->
+ case Node of
+ {type, _, map_field_exact, [_, Value]} ->
+ Value;
+ _ ->
+ (data(Node))#map_type_exact.value
+ end.
+
+
+%% =====================================================================
+%% @equiv map_type(any_size)
+
+map_type() ->
+ map_type(any_size).
+
+%% =====================================================================
+%% @doc Creates an abstract type map. If `Fields' is
+%% `[F1, ..., Fn]', the result represents
+%% "<code>#{<em>F1</em>, ..., <em>Fn</em>}</code>";
+%% otherwise, if `Fields' is `any_size', it represents
+%% "<code>map()</code>".
+%%
+%% @see map_type_fields/1
+
+%% type(Node) = map_type
+%% data(Node) = Fields
+%%
+%% Fields = any_size | [syntaxTree()]
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, map, [Field]}
+%% {type, Pos, map, any}
+%%
+%% Field = erl_parse()
+
+-spec map_type('any_size' | [syntaxTree()]) -> syntaxTree().
+
+map_type(Fields) ->
+ tree(map_type, Fields).
+
+revert_map_type(Node) ->
+ Pos = get_pos(Node),
+ {type, Pos, map, map_type_fields(Node)}.
+
+
+%% =====================================================================
+%% @doc Returns the list of field subtrees of a `map_type' node.
+%% If `Node' represents "<code>map()</code>", `any_size' is returned;
+%% otherwise, if `Node' represents
+%% "<code>#{<em>F1</em>, ..., <em>Fn</em>}</code>",
+%% `[F1, ..., Fn]' is returned.
+%%
+%% @see map_type/0
+%% @see map_type/1
+
+-spec map_type_fields(syntaxTree()) -> 'any_size' | [syntaxTree()].
+
+map_type_fields(Node) ->
+ case unwrap(Node) of
+ {type, _, map, Fields} when is_list(Fields) ->
+ Fields;
+ {type, _, map, any} ->
+ any_size;
+ Node1 ->
+ data(Node1)
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract range type. The result represents
+%% "<code><em>Low</em> .. <em>High</em></code>".
+%%
+%% @see integer_range_type_low/1
+%% @see integer_range_type_high/1
+
+-record(integer_range_type, {low :: syntaxTree(),
+ high :: syntaxTree()}).
+
+%% type(Node) = integer_range_type
+%% data(Node) = #integer_range_type{low :: Low, high :: High}
+%%
+%% Low = syntaxTree()
+%% High = syntaxTree()
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, range, [Low, High]}
+%%
+%% Low = erl_parse()
+%% High = erl_parse()
+
+-spec integer_range_type(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+integer_range_type(Low, High) ->
+ tree(integer_range_type, #integer_range_type{low = Low, high = High}).
+
+revert_integer_range_type(Node) ->
+ Pos = get_pos(Node),
+ Low = integer_range_type_low(Node),
+ High = integer_range_type_high(Node),
+ {type, Pos, range, [Low, High]}.
+
+
+%% =====================================================================
+%% @doc Returns the low limit of an `integer_range_type' node.
+%%
+%% @see integer_range_type/2
+
+-spec integer_range_type_low(syntaxTree()) -> syntaxTree().
+
+integer_range_type_low(Node) ->
+ case unwrap(Node) of
+ {type, _, range, [Low, _]} ->
+ Low;
+ Node1 ->
+ (data(Node1))#integer_range_type.low
+ end.
+
+%% =====================================================================
+%% @doc Returns the high limit of an `integer_range_type' node.
+%%
+%% @see integer_range_type/2
+
+-spec integer_range_type_high(syntaxTree()) -> syntaxTree().
+
+integer_range_type_high(Node) ->
+ case unwrap(Node) of
+ {type, _, range, [_, High]} ->
+ High;
+ Node1 ->
+ (data(Node1))#integer_range_type.high
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract record type. If `Fields' is
+%% `[F1, ..., Fn]', the result represents
+%% "<code>#<em>Name</em>{<em>F1</em>, ..., <em>Fn</em>}</code>".
+%%
+%% @see record_type_name/1
+%% @see record_type_fields/1
+
+-record(record_type, {name :: syntaxTree(),
+ fields :: [syntaxTree()]}).
+
+%% type(Node) = record_type
+%% data(Node) = #record_type{name = Name, fields = Fields}
+%%
+%% Name = syntaxTree()
+%% Fields = [syntaxTree()]
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, record, [Name|Fields]}
+%%
+%% Name = erl_parse()
+%% Fields = [erl_parse()]
+
+-spec record_type(syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+record_type(Name, Fields) ->
+ tree(record_type, #record_type{name = Name, fields = Fields}).
+
+revert_record_type(Node) ->
+ Pos = get_pos(Node),
+ Name = record_type_name(Node),
+ Fields = record_type_fields(Node),
+ {type, Pos, record, [Name | Fields]}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `record_type' node.
+%%
+%% @see record_type/2
+
+-spec record_type_name(syntaxTree()) -> syntaxTree().
+
+record_type_name(Node) ->
+ case unwrap(Node) of
+ {type, _, record, [Name|_]} ->
+ Name;
+ Node1 ->
+ (data(Node1))#record_type.name
+ end.
+
+%% =====================================================================
+%% @doc Returns the fields subtree of a `record_type' node.
+%%
+%% @see record_type/2
+
+-spec record_type_fields(syntaxTree()) -> [syntaxTree()].
+
+record_type_fields(Node) ->
+ case unwrap(Node) of
+ {type, _, record, [_|Fields]} ->
+ Fields;
+ Node1 ->
+ (data(Node1))#record_type.fields
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract record type field. The result represents
+%% "<code><em>Name</em> :: <em>Type</em></code>".
+%%
+%% @see record_type_field_name/1
+%% @see record_type_field_type/1
+
+-record(record_type_field, {name :: syntaxTree(),
+ type :: syntaxTree()}).
+
+%% type(Node) = record_type_field
+%% data(Node) = #record_type_field{name = Name, type = Type}
+%%
+%% Name = syntaxTree()
+%% Type = syntaxTree()
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, field_type, [Name, Type]}
+%%
+%% Name = erl_parse()
+%% Type = erl_parse()
+
+-spec record_type_field(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+record_type_field(Name, Type) ->
+ tree(record_type_field, #record_type_field{name = Name, type = Type}).
+
+revert_record_type_field(Node) ->
+ Pos = get_pos(Node),
+ Name = record_type_field_name(Node),
+ Type = record_type_field_type(Node),
+ {type, Pos, field_type, [Name, Type]}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `record_type_field' node.
+%%
+%% @see record_type_field/2
+
+-spec record_type_field_name(syntaxTree()) -> syntaxTree().
+
+record_type_field_name(Node) ->
+ case unwrap(Node) of
+ {type, _, field_type, [Name, _]} ->
+ Name;
+ Node1 ->
+ (data(Node1))#record_type_field.name
+ end.
+
+%% =====================================================================
+%% @doc Returns the type subtree of a `record_type_field' node.
+%%
+%% @see record_type_field/2
+
+-spec record_type_field_type(syntaxTree()) -> syntaxTree().
+
+record_type_field_type(Node) ->
+ case unwrap(Node) of
+ {type, _, field_type, [_, Type]} ->
+ Type;
+ Node1 ->
+ (data(Node1))#record_type_field.type
+ end.
+
+
+%% =====================================================================
+%% @equiv tuple_type(any_size)
+
+tuple_type() ->
+ tuple_type(any_size).
+
+%% =====================================================================
+%% @doc Creates an abstract type tuple. If `Elements' is
+%% `[T1, ..., Tn]', the result represents
+%% "<code>{<em>T1</em>, ..., <em>Tn</em>}</code>";
+%% otherwise, if `Elements' is `any_size', it represents
+%% "<code>tuple()</code>".
+%%
+%% @see tuple_type_elements/1
+
+%% type(Node) = tuple_type
+%% data(Node) = Elements
+%%
+%% Elements = any_size | [syntaxTree()]
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, tuple, [Element]}
+%% {type, Pos, tuple, any}
+%%
+%% Element = erl_parse()
+
+-spec tuple_type(any_size | [syntaxTree()]) -> syntaxTree().
+
+tuple_type(Elements) ->
+ tree(tuple_type, Elements).
+
+revert_tuple_type(Node) ->
+ Pos = get_pos(Node),
+ {type, Pos, tuple, tuple_type_elements(Node)}.
+
+
+%% =====================================================================
+%% @doc Returns the list of type element subtrees of a `tuple_type' node.
+%% If `Node' represents "<code>tuple()</code>", `any_size' is returned;
+%% otherwise, if `Node' represents
+%% "<code>{<em>T1</em>, ..., <em>Tn</em>}</code>",
+%% `[T1, ..., Tn]' is returned.
+%%
+%% @see tuple_type/0
+%% @see tuple_type/1
+
+-spec tuple_type_elements(syntaxTree()) -> 'any_size' | [syntaxTree()].
+
+tuple_type_elements(Node) ->
+ case unwrap(Node) of
+ {type, _, tuple, Elements} when is_list(Elements) ->
+ Elements;
+ {type, _, tuple, any} ->
+ any_size;
+ Node1 ->
+ data(Node1)
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract type union. If `Types' is
+%% `[T1, ..., Tn]', the result represents
+%% "<code><em>T1</em> | ... | <em>Tn</em></code>".
+%%
+%% @see type_union_types/1
+
+%% type(Node) = type_union
+%% data(Node) = Types
+%%
+%% Types = [syntaxTree()]
+%%
+%% `erl_parse' representation:
+%%
+%% {type, Pos, union, Elements}
+%%
+%% Elements = [erl_parse()]
+
+-spec type_union([syntaxTree()]) -> syntaxTree().
+
+type_union(Types) ->
+ tree(type_union, Types).
+
+revert_type_union(Node) ->
+ Pos = get_pos(Node),
+ {type, Pos, union, type_union_types(Node)}.
+
+
+%% =====================================================================
+%% @doc Returns the list of type subtrees of a `type_union' node.
+%%
+%% @see type_union/1
+
+-spec type_union_types(syntaxTree()) -> [syntaxTree()].
+
+type_union_types(Node) ->
+ case unwrap(Node) of
+ {type, _, union, Types} when is_list(Types) ->
+ Types;
+ Node1 ->
+ data(Node1)
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract user type. If `Arguments' is
+%% `[T1, ..., Tn]', the result represents
+%% "<code><em>TypeName</em>(<em>T1</em>, ...<em>Tn</em>)</code>".
+%%
+%% @see type_application/2
+%% @see user_type_application_name/1
+%% @see user_type_application_arguments/1
+
+-record(user_type_application, {type_name :: syntaxTree(),
+ arguments :: [syntaxTree()]}).
+
+%% type(Node) = user_type_application
+%% data(Node) = #user_type_application{type_name :: TypeName,
+%% arguments :: Arguments}
+%%
+%% TypeName = syntaxTree()
+%% Arguments = [syntaxTree()]
+%%
+%% `erl_parse' representation:
+%%
+%% {user_type, Pos, Name, Arguments}
+%%
+%% Name = erl_parse()
+%% Arguments = [Type]
+%% Type = erl_parse()
+
+-spec user_type_application(syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+user_type_application(TypeName, Arguments) ->
+ tree(user_type_application,
+ #user_type_application{type_name = TypeName, arguments = Arguments}).
+
+revert_user_type_application(Node) ->
+ Pos = get_pos(Node),
+ TypeName = user_type_application_name(Node),
+ Arguments = user_type_application_arguments(Node),
+ {user_type, Pos, atom_value(TypeName), Arguments}.
+
+
+%% =====================================================================
+%% @doc Returns the type name subtree of a `user_type_application' node.
+%%
+%% @see user_type_application/2
+
+-spec user_type_application_name(syntaxTree()) -> syntaxTree().
+
+user_type_application_name(Node) ->
+ case unwrap(Node) of
+ {user_type, Pos, Name, _} ->
+ set_pos(atom(Name), Pos);
+ Node1 ->
+ (data(Node1))#user_type_application.type_name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the arguments subtrees of a `user_type_application' node.
+%%
+%% @see user_type_application/2
+
+-spec user_type_application_arguments(syntaxTree()) -> [syntaxTree()].
+
+user_type_application_arguments(Node) ->
+ case unwrap(Node) of
+ {user_type, _, _, Arguments} ->
+ Arguments;
+ Node1 ->
+ (data(Node1))#user_type_application.arguments
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract typed record field specification. The
+%% result represents "<code><em>Field</em> :: <em>Type</em></code>".
+%%
+%% @see typed_record_field_body/1
+%% @see typed_record_field_type/1
+
+-record(typed_record_field, {body :: syntaxTree(),
+ type :: syntaxTree()}).
+
+%% type(Node) = typed_record_field
+%% data(Node) = #typed_record_field{body :: Field
+%% type = Type}
+%%
+%% Field = syntaxTree()
+%% Type = syntaxTree()
+
+-spec typed_record_field(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+typed_record_field(Field, Type) ->
+ tree(typed_record_field,
+ #typed_record_field{body = Field, type = Type}).
+
+
+%% =====================================================================
+%% @doc Returns the field subtree of a `typed_record_field' node.
+%%
+%% @see typed_record_field/2
+
+-spec typed_record_field_body(syntaxTree()) -> syntaxTree().
+
+typed_record_field_body(Node) ->
+ (data(Node))#typed_record_field.body.
+
+
+%% =====================================================================
+%% @doc Returns the type subtree of a `typed_record_field' node.
+%%
+%% @see typed_record_field/2
+
+-spec typed_record_field_type(syntaxTree()) -> syntaxTree().
+
+typed_record_field_type(Node) ->
+ (data(Node))#typed_record_field.type.
+
%% =====================================================================
%% @doc Creates an abstract list comprehension. If `Body' is
@@ -6168,6 +7411,8 @@ revert(Node) ->
revert_root(Node) ->
case type(Node) of
+ annotated_type ->
+ revert_annotated_type(Node);
application ->
revert_application(Node);
atom ->
@@ -6182,6 +7427,8 @@ revert_root(Node) ->
revert_binary_field(Node);
binary_generator ->
revert_binary_generator(Node);
+ bitstring_type ->
+ revert_bitstring_type(Node);
block_expr ->
revert_block_expr(Node);
case_expr ->
@@ -6194,6 +7441,10 @@ revert_root(Node) ->
revert_clause(Node);
cond_expr ->
revert_cond_expr(Node);
+ constrained_function_type ->
+ revert_constrained_function_type(Node);
+ constraint ->
+ revert_constraint(Node);
eof_marker ->
revert_eof_marker(Node);
error_marker ->
@@ -6202,8 +7453,12 @@ revert_root(Node) ->
revert_float(Node);
fun_expr ->
revert_fun_expr(Node);
+ fun_type ->
+ revert_fun_type(Node);
function ->
revert_function(Node);
+ function_type ->
+ revert_function_type(Node);
generator ->
revert_generator(Node);
if_expr ->
@@ -6214,6 +7469,8 @@ revert_root(Node) ->
revert_infix_expr(Node);
integer ->
revert_integer(Node);
+ integer_range_type ->
+ revert_integer_range_type(Node);
list ->
revert_list(Node);
list_comp ->
@@ -6224,6 +7481,12 @@ revert_root(Node) ->
revert_map_field_assoc(Node);
map_field_exact ->
revert_map_field_exact(Node);
+ map_type ->
+ revert_map_type(Node);
+ map_type_assoc ->
+ revert_map_type_assoc(Node);
+ map_type_exact ->
+ revert_map_type_exact(Node);
match_expr ->
revert_match_expr(Node);
module_qualifier ->
@@ -6244,14 +7507,26 @@ revert_root(Node) ->
revert_record_expr(Node);
record_index_expr ->
revert_record_index_expr(Node);
+ record_type ->
+ revert_record_type(Node);
+ record_type_field ->
+ revert_record_type_field(Node);
+ type_application ->
+ revert_type_application(Node);
+ type_union ->
+ revert_type_union(Node);
string ->
revert_string(Node);
try_expr ->
revert_try_expr(Node);
tuple ->
revert_tuple(Node);
+ tuple_type ->
+ revert_tuple_type(Node);
underscore ->
revert_underscore(Node);
+ user_type_application ->
+ revert_user_type_application(Node);
variable ->
revert_variable(Node);
warning_marker ->
@@ -6379,6 +7654,9 @@ subtrees(T) ->
[];
false ->
case type(T) of
+ annotated_type ->
+ [[annotated_type_name(T)],
+ [annotated_type_body(T)]];
application ->
[[application_operator(T)],
application_arguments(T)];
@@ -6407,6 +7685,9 @@ subtrees(T) ->
binary_generator ->
[[binary_generator_pattern(T)],
[binary_generator_body(T)]];
+ bitstring_type ->
+ [[bitstring_type_m(T)],
+ [bitstring_type_n(T)]];
block_expr ->
[block_expr_body(T)];
case_expr ->
@@ -6429,14 +7710,30 @@ subtrees(T) ->
[cond_expr_clauses(T)];
conjunction ->
[conjunction_body(T)];
+ constrained_function_type ->
+ C = constrained_function_type_argument(T),
+ [[constrained_function_type_body(T)],
+ conjunction_body(C)];
+ constraint ->
+ [[constraint_argument(T)],
+ constraint_body(T)];
disjunction ->
[disjunction_body(T)];
form_list ->
[form_list_elements(T)];
fun_expr ->
[fun_expr_clauses(T)];
+ fun_type ->
+ [];
function ->
[[function_name(T)], function_clauses(T)];
+ function_type ->
+ case function_type_arguments(T) of
+ any_arity ->
+ [[function_type_return(T)]];
+ As ->
+ [As,[function_type_return(T)]]
+ end;
generator ->
[[generator_pattern(T)], [generator_body(T)]];
if_expr ->
@@ -6447,6 +7744,9 @@ subtrees(T) ->
[[infix_expr_left(T)],
[infix_expr_operator(T)],
[infix_expr_right(T)]];
+ integer_range_type ->
+ [[integer_range_type_low(T)],
+ [integer_range_type_high(T)]];
list ->
case list_suffix(T) of
none ->
@@ -6476,6 +7776,14 @@ subtrees(T) ->
map_field_exact ->
[[map_field_exact_name(T)],
[map_field_exact_value(T)]];
+ map_type ->
+ [map_type_fields(T)];
+ map_type_assoc ->
+ [[map_type_assoc_name(T)],
+ [map_type_assoc_value(T)]];
+ map_type_exact ->
+ [[map_type_exact_name(T)],
+ [map_type_exact_value(T)]];
match_expr ->
[[match_expr_pattern(T)],
[match_expr_body(T)]];
@@ -6523,6 +7831,12 @@ subtrees(T) ->
record_index_expr ->
[[record_index_expr_type(T)],
[record_index_expr_field(T)]];
+ record_type ->
+ [[record_type_name(T)],
+ record_type_fields(T)];
+ record_type_field ->
+ [[record_type_field_name(T)],
+ [record_type_field_type(T)]];
size_qualifier ->
[[size_qualifier_body(T)],
[size_qualifier_argument(T)]];
@@ -6532,7 +7846,20 @@ subtrees(T) ->
try_expr_handlers(T),
try_expr_after(T)];
tuple ->
- [tuple_elements(T)]
+ [tuple_elements(T)];
+ tuple_type ->
+ [tuple_type_elements(T)];
+ type_application ->
+ [[type_application_name(T)],
+ type_application_arguments(T)];
+ type_union ->
+ [type_union_types(T)];
+ typed_record_field ->
+ [[typed_record_field_body(T)],
+ [typed_record_field_type(T)]];
+ user_type_application ->
+ [[user_type_application_name(T)],
+ user_type_application_arguments(T)]
end
end.
@@ -6576,6 +7903,7 @@ update_tree(Node, Groups) ->
-spec make_tree(atom(), [[syntaxTree()]]) -> syntaxTree().
+make_tree(annotated_type, [[N], [T]]) -> annotated_type(N, T);
make_tree(application, [[F], A]) -> application(F, A);
make_tree(arity_qualifier, [[N], [A]]) -> arity_qualifier(N, A);
make_tree(attribute, [[N]]) -> attribute(N);
@@ -6585,6 +7913,7 @@ make_tree(binary_comp, [[T], B]) -> binary_comp(T, B);
make_tree(binary_field, [[B]]) -> binary_field(B);
make_tree(binary_field, [[B], Ts]) -> binary_field(B, Ts);
make_tree(binary_generator, [[P], [E]]) -> binary_generator(P, E);
+make_tree(bitstring_type, [[M], [N]]) -> bitstring_type(M, N);
make_tree(block_expr, [B]) -> block_expr(B);
make_tree(case_expr, [[A], C]) -> case_expr(A, C);
make_tree(catch_expr, [[B]]) -> catch_expr(B);
@@ -6593,14 +7922,20 @@ make_tree(clause, [P, B]) -> clause(P, none, B);
make_tree(clause, [P, [G], B]) -> clause(P, G, B);
make_tree(cond_expr, [C]) -> cond_expr(C);
make_tree(conjunction, [E]) -> conjunction(E);
+make_tree(constrained_function_type, [[F],C]) ->
+ constrained_function_type(F, C);
+make_tree(constraint, [[N], Ts]) -> constraint(N, Ts);
make_tree(disjunction, [E]) -> disjunction(E);
make_tree(form_list, [E]) -> form_list(E);
make_tree(fun_expr, [C]) -> fun_expr(C);
make_tree(function, [[N], C]) -> function(N, C);
+make_tree(function_type, [[T]]) -> function_type(T);
+make_tree(function_type, [A,[T]]) -> function_type(A, T);
make_tree(generator, [[P], [E]]) -> generator(P, E);
make_tree(if_expr, [C]) -> if_expr(C);
make_tree(implicit_fun, [[N]]) -> implicit_fun(N);
make_tree(infix_expr, [[L], [F], [R]]) -> infix_expr(L, F, R);
+make_tree(integer_range_type, [[L],[H]]) -> integer_range_type(L, H);
make_tree(list, [P]) -> list(P);
make_tree(list, [P, [S]]) -> list(P, S);
make_tree(list_comp, [[T], B]) -> list_comp(T, B);
@@ -6610,6 +7945,9 @@ 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(map_type, [Fs]) -> map_type(Fs);
+make_tree(map_type_assoc, [[N],[V]]) -> map_type_assoc(N, V);
+make_tree(map_type_exact, [[N],[V]]) -> map_type_exact(N, 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);
@@ -6625,9 +7963,16 @@ make_tree(record_field, [[N]]) -> record_field(N);
make_tree(record_field, [[N], [E]]) -> record_field(N, E);
make_tree(record_index_expr, [[T], [F]]) ->
record_index_expr(T, F);
+make_tree(record_type, [[N],Fs]) -> record_type(N, Fs);
+make_tree(record_type_field, [[N],[T]]) -> record_type_field(N, T);
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(tuple, [E]) -> tuple(E);
+make_tree(tuple_type, [Es]) -> tuple_type(Es);
+make_tree(type_application, [[N], Ts]) -> type_application(N, Ts);
+make_tree(type_union, [Es]) -> type_union(Es);
+make_tree(typed_record_field, [[F],[T]]) -> typed_record_field(F, T);
+make_tree(user_type_application, [[N], Ts]) -> user_type_application(N, Ts).
%% =====================================================================
@@ -6954,6 +8299,7 @@ fold_variable_names(Vs) ->
unfold_variable_names(Vs, Pos) ->
[set_pos(variable(V), Pos) || V <- Vs].
+
%% Support functions for transforming lists of record field definitions.
%%
%% There is no unique representation for field definitions in the
@@ -6968,6 +8314,16 @@ fold_record_fields(Fs) ->
[fold_record_field(F) || F <- Fs].
fold_record_field(F) ->
+ case type(F) of
+ typed_record_field ->
+ Field = fold_record_field_1(typed_record_field_body(F)),
+ Type = typed_record_field_type(F),
+ {typed_record_field, Field, Type};
+ record_field ->
+ fold_record_field_1(F)
+ end.
+
+fold_record_field_1(F) ->
Pos = get_pos(F),
Name = record_field_name(F),
case record_field_value(F) of
@@ -6980,10 +8336,11 @@ fold_record_field(F) ->
unfold_record_fields(Fs) ->
[unfold_record_field(F) || F <- Fs].
-unfold_record_field({typed_record_field, Field, _Type}) ->
- unfold_record_field_1(Field);
+unfold_record_field({typed_record_field, Field, Type}) ->
+ F = unfold_record_field_1(Field),
+ set_pos(typed_record_field(F, Type), get_pos(F));
unfold_record_field(Field) ->
- unfold_record_field_1(Field).
+ unfold_record_field_1(Field).
unfold_record_field_1({record_field, Pos, Name}) ->
set_pos(record_field(Name), Pos);
@@ -7010,5 +8367,4 @@ unfold_binary_field_type({Type, Size}, Pos) ->
unfold_binary_field_type(Type, Pos) ->
set_pos(atom(Type), Pos).
-
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 58c4cc5244..9815559779 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -36,6 +36,7 @@
analyze_import_attribute/1, analyze_module_attribute/1,
analyze_record_attribute/1, analyze_record_expr/1,
analyze_record_field/1, analyze_wild_attribute/1, annotate_bindings/1,
+ analyze_type_application/1, analyze_type_name/1,
annotate_bindings/2, fold/3, fold_subtrees/3, foldl_listlist/3,
function_name_expansions/1, is_fail_expr/1, limit/2, limit/3,
map/2, map_subtrees/2, mapfold/3, mapfold_subtrees/3,
@@ -1029,14 +1030,17 @@ is_fail_expr(E) ->
%% <dt>`{records, Records}'</dt>
%% <dd><ul>
%% <li>`Records = [{atom(), Fields}]'</li>
-%% <li>`Fields = [{atom(), Default}]'</li>
+%% <li>`Fields = [{atom(), {Default, Type}}]'</li>
%% <li>`Default = none | syntaxTree()'</li>
+%% <li>`Type = none | syntaxTree()'</li>
%% </ul>
%% `Records' is a list of pairs representing the names
%% and corresponding field declarations of all record declaration
%% attributes occurring in `Forms'. For fields declared
%% without a default value, the corresponding value for
-%% `Default' is the atom `none' (cf.
+%% `Default' is the atom `none'. Similarly, for fields declared
+%% without a type, the corresponding value for `Type' is the
+%% atom `none' (cf.
%% `analyze_record_attribute/1'). We do not guarantee
%% that each record name occurs at most once in the list. The
%% order of listing is not defined.</dd>
@@ -1055,9 +1059,9 @@ is_fail_expr(E) ->
%%
%% @see analyze_wild_attribute/1
%% @see analyze_export_attribute/1
+%% @see analyze_function/1
%% @see analyze_import_attribute/1
%% @see analyze_record_attribute/1
-%% @see analyze_function/1
%% @see erl_syntax:error_marker_info/1
%% @see erl_syntax:warning_marker_info/1
@@ -1102,8 +1106,6 @@ collect_attribute(file, _, Info) ->
Info;
collect_attribute(record, {R, L}, Info) ->
finfo_add_record(R, L, Info);
-collect_attribute(spec, _, Info) ->
- Info;
collect_attribute(_, {N, V}, Info) ->
finfo_add_attribute(N, V, Info).
@@ -1114,12 +1116,15 @@ collect_attribute(_, {N, V}, Info) ->
module_imports = [] :: [atom()],
imports = [] :: [{atom(), [{atom(), arity()}]}],
attributes = [] :: [{atom(), term()}],
- records = [] :: [{atom(), [{atom(), field_default()}]}],
+ records = [] :: [{atom(), [{atom(),
+ field_default(),
+ field_type()}]}],
errors = [] :: [term()],
warnings = [] :: [term()],
functions = [] :: [{atom(), arity()}]}).
-type field_default() :: 'none' | erl_syntax:syntaxTree().
+-type field_type() :: 'none' | erl_syntax:syntaxTree().
new_finfo() ->
#forms{}.
@@ -1326,8 +1331,6 @@ analyze_attribute(file, Node) ->
analyze_file_attribute(Node);
analyze_attribute(record, Node) ->
analyze_record_attribute(Node);
-analyze_attribute(spec, _Node) ->
- spec;
analyze_attribute(_, Node) ->
%% A "wild" attribute (such as e.g. a `compile' directive).
analyze_wild_attribute(Node).
@@ -1523,6 +1526,55 @@ analyze_import_attribute(Node) ->
%% =====================================================================
+%% @spec analyze_type_name(Node::syntaxTree()) -> TypeName
+%%
+%% TypeName = atom()
+%% | {atom(), integer()}
+%% | {ModuleName, {atom(), integer()}}
+%% ModuleName = atom()
+%%
+%% @doc Returns the type name represented by a syntax tree. If
+%% `Node' represents a type name, such as
+%% "`foo/1'" or "`bloggs:fred/2'", a uniform
+%% representation of that name is returned.
+%%
+%% The evaluation throws `syntax_error' if
+%% `Node' does not represent a well-formed type name.
+
+-spec analyze_type_name(erl_syntax:syntaxTree()) -> typeName().
+
+analyze_type_name(Node) ->
+ case erl_syntax:type(Node) of
+ atom ->
+ erl_syntax:atom_value(Node);
+ arity_qualifier ->
+ A = erl_syntax:arity_qualifier_argument(Node),
+ N = erl_syntax:arity_qualifier_body(Node),
+
+ case ((erl_syntax:type(A) =:= integer)
+ and (erl_syntax:type(N) =:= atom))
+ of
+ true ->
+ append_arity(erl_syntax:integer_value(A),
+ erl_syntax:atom_value(N));
+ _ ->
+ throw(syntax_error)
+ end;
+ module_qualifier ->
+ M = erl_syntax:module_qualifier_argument(Node),
+ case erl_syntax:type(M) of
+ atom ->
+ N = erl_syntax:module_qualifier_body(Node),
+ N1 = analyze_type_name(N),
+ {erl_syntax:atom_value(M), N1};
+ _ ->
+ throw(syntax_error)
+ end;
+ _ ->
+ throw(syntax_error)
+ end.
+
+%% =====================================================================
%% @spec analyze_wild_attribute(Node::syntaxTree()) -> {atom(), term()}
%%
%% @doc Returns the name and value of a "wild" attribute. The result is
@@ -1547,6 +1599,7 @@ analyze_wild_attribute(Node) ->
atom ->
case erl_syntax:attribute_arguments(Node) of
[V] ->
+ %% Note: does not work well with macros.
case catch {ok, erl_syntax:concrete(V)} of
{ok, Val} ->
{erl_syntax:atom_value(N), Val};
@@ -1568,17 +1621,22 @@ analyze_wild_attribute(Node) ->
%% @spec analyze_record_attribute(Node::syntaxTree()) ->
%% {atom(), Fields}
%%
-%% Fields = [{atom(), none | syntaxTree()}]
+%% Fields = [{atom(), {Default, Type}}]
+%% Default = none | syntaxTree()
+%% Type = none | syntaxTree()
%%
%% @doc Returns the name and the list of fields of a record declaration
%% attribute. The result is a pair `{Name, Fields}', if
%% `Node' represents "`-record(Name, {...}).'",
%% where `Fields' is a list of pairs `{Label,
-%% Default}' for each field "`Label'" or "`Label =
-%% <em>Default</em>'" in the declaration, listed in left-to-right
+%% {Default, Type}}' for each field "`Label'", "`Label =
+%% <em>Default</em>'", "`Label :: <em>Type</em>'", or
+%% "`Label = <em>Default</em> :: <em>Type</em>'" in the declaration,
+%% listed in left-to-right
%% order. If the field has no default-value declaration, the value for
-%% `Default' will be the atom `none'. We do not
-%% guarantee that each label occurs at most one in the list.
+%% `Default' will be the atom `none'. If the field has no type declaration,
+%% the value for `Type' will be the atom `none'. We do not
+%% guarantee that each label occurs at most once in the list.
%%
%% The evaluation throws `syntax_error' if
%% `Node' does not represent a well-formed record declaration
@@ -1587,7 +1645,9 @@ analyze_wild_attribute(Node) ->
%% @see analyze_attribute/1
%% @see analyze_record_field/1
--type fields() :: [{atom(), 'none' | erl_syntax:syntaxTree()}].
+-type field() :: {atom(), {field_default(), field_type()}}.
+
+-type fields() :: [field()].
-spec analyze_record_attribute(erl_syntax:syntaxTree()) -> {atom(), fields()}.
@@ -1625,7 +1685,7 @@ analyze_record_attribute_tuple(Node) ->
%% {atom(), Info} | atom()
%%
%% Info = {atom(), [{atom(), Value}]} | {atom(), atom()} | atom()
-%% Value = none | syntaxTree()
+%% Value = syntaxTree()
%%
%% @doc Returns the record name and field name/names of a record
%% expression. If `Node' has type `record_expr',
@@ -1645,9 +1705,9 @@ analyze_record_attribute_tuple(Node) ->
%%
%% For a `record_expr' node, `Info' represents
%% the record name and the list of descriptors for the involved fields,
-%% listed in the order they appear. (See
-%% `analyze_record_field/1' for details on the field
-%% descriptors). For a `record_access' node,
+%% listed in the order they appear. A field descriptor is a pair
+%% `{Label, Value}', if `Node' represents "`Label = <em>Value</em>'".
+%% For a `record_access' node,
%% `Info' represents the record name and the field name. For a
%% `record_index_expr' node, `Info' represents the
%% record name and the name field name.
@@ -1659,7 +1719,7 @@ analyze_record_attribute_tuple(Node) ->
%% @see analyze_record_attribute/1
%% @see analyze_record_field/1
--type info() :: {atom(), [{atom(), 'none' | erl_syntax:syntaxTree()}]}
+-type info() :: {atom(), [{atom(), erl_syntax:syntaxTree()}]}
| {atom(), atom()} | atom().
-spec analyze_record_expr(erl_syntax:syntaxTree()) -> {atom(), info()} | atom().
@@ -1670,8 +1730,9 @@ analyze_record_expr(Node) ->
A = erl_syntax:record_expr_type(Node),
case erl_syntax:type(A) of
atom ->
- Fs = [analyze_record_field(F)
- || F <- erl_syntax:record_expr_fields(Node)],
+ Fs0 = [analyze_record_field(F)
+ || F <- erl_syntax:record_expr_fields(Node)],
+ Fs = [{N, D} || {N, {D, _T}} <- Fs0],
{record_expr, {erl_syntax:atom_value(A), Fs}};
_ ->
throw(syntax_error)
@@ -1713,16 +1774,19 @@ analyze_record_expr(Node) ->
end.
%% =====================================================================
-%% @spec analyze_record_field(Node::syntaxTree()) -> {atom(), Value}
+%% @spec analyze_record_field(Node::syntaxTree()) -> {atom(), {Default, Type}}
%%
-%% Value = none | syntaxTree()
+%% Default = none | syntaxTree()
+%% Type = none | syntaxTree()
%%
-%% @doc Returns the label and value-expression of a record field
-%% specifier. The result is a pair `{Label, Value}', if
-%% `Node' represents "`Label = <em>Value</em>'" or
-%% "`Label'", where in the first case, `Value' is
-%% a syntax tree, and in the second case `Value' is
-%% `none'.
+%% @doc Returns the label, value-expression, and type of a record field
+%% specifier. The result is a pair `{Label, {Default, Type}}', if
+%% `Node' represents "`Label'", "`Label = <em>Default</em>'",
+%% "`Label :: <em>Type</em>'", or
+%% "`Label = <em>Default</em> :: <em>Type</em>'".
+%% If the field has no value-expression, the value for
+%% `Default' will be the atom `none'. If the field has no type,
+%% the value for `Type' will be the atom `none'.
%%
%% The evaluation throws `syntax_error' if
%% `Node' does not represent a well-formed record field
@@ -1731,8 +1795,7 @@ analyze_record_expr(Node) ->
%% @see analyze_record_attribute/1
%% @see analyze_record_expr/1
--spec analyze_record_field(erl_syntax:syntaxTree()) ->
- {atom(), 'none' | erl_syntax:syntaxTree()}.
+-spec analyze_record_field(erl_syntax:syntaxTree()) -> field().
analyze_record_field(Node) ->
case erl_syntax:type(Node) of
@@ -1741,10 +1804,15 @@ analyze_record_field(Node) ->
case erl_syntax:type(A) of
atom ->
T = erl_syntax:record_field_value(Node),
- {erl_syntax:atom_value(A), T};
+ {erl_syntax:atom_value(A), {T, none}};
_ ->
throw(syntax_error)
end;
+ typed_record_field ->
+ F = erl_syntax:typed_record_field_body(Node),
+ {N, {V, _none}} = analyze_record_field(F),
+ T = erl_syntax:typed_record_field_type(Node),
+ {N, {V, T}};
_ ->
throw(syntax_error)
end.
@@ -1887,6 +1955,55 @@ analyze_application(Node) ->
%% =====================================================================
+%% @spec analyze_type_application(Node::syntaxTree()) -> typeName()
+%%
+%% TypeName = {atom(), integer()}
+%% | {ModuleName, {atom(), integer()}}
+%% ModuleName = atom()
+%%
+%% @doc Returns the name of a used type. The result is a
+%% representation of the name of the used pre-defined or local type `N/A',
+%% if `Node' represents a local (user) type application
+%% "`<em>N</em>(<em>T_1</em>, ..., <em>T_A</em>)'", or
+%% a representation of the name of the used remote type `M:N/A'
+%% if `Node' represents a remote user type application
+%% "`<em>M</em>:<em>N</em>(<em>T_1</em>, ..., <em>T_A</em>)'".
+%%
+%% The evaluation throws `syntax_error' if `Node' does not represent a
+%% well-formed (user) type application expression.
+%%
+%% @see analyze_type_name/1
+
+-type typeName() :: atom() | {module(), atom(), arity()} | {atom(), arity()}.
+
+-spec analyze_type_application(erl_syntax:syntaxTree()) -> typeName().
+
+analyze_type_application(Node) ->
+ case erl_syntax:type(Node) of
+ type_application ->
+ A = length(erl_syntax:type_application_arguments(Node)),
+ N = erl_syntax:type_application_name(Node),
+ case catch {ok, analyze_type_name(N)} of
+ {ok, TypeName} ->
+ append_arity(A, TypeName);
+ _ ->
+ throw(syntax_error)
+ end;
+ user_type_application ->
+ A = length(erl_syntax:user_type_application_arguments(Node)),
+ N = erl_syntax:user_type_application_name(Node),
+ case catch {ok, analyze_type_name(N)} of
+ {ok, TypeName} ->
+ append_arity(A, TypeName);
+ _ ->
+ throw(syntax_error)
+ end;
+ _ ->
+ throw(syntax_error)
+ end.
+
+
+%% =====================================================================
%% @spec function_name_expansions(Names::[Name]) -> [{ShortName, Name}]
%%
%% Name = ShortName | {atom(), Name}
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index 4557678f9d..1d14bd7c3a 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -2612,6 +2612,19 @@ get_module_info(Forms) ->
fold_record_fields(Rs) ->
[{N, [fold_record_field(F) || F <- Fs]} || {N, Fs} <- Rs].
+fold_record_field({_Name, {none, _Type}} = None) ->
+ None;
+fold_record_field({Name, {F, Type}}) ->
+ case erl_syntax:is_literal(F) of
+ true ->
+ {Name, {value, erl_syntax:concrete(F)}, Type};
+ false ->
+ %% The default value for the field is not a constant, so we
+ %% represent it by a hash value instead. (We don't want to
+ %% do this in the general case.)
+ {Name, {hash, erlang:phash(F, 16#ffffff)}, Type}
+ end;
+%% The following two clauses handle code before Erlang/OTP 19.0.
fold_record_field({_Name, none} = None) ->
None;
fold_record_field({Name, F}) ->
diff --git a/lib/syntax_tools/src/syntax_tools.app.src b/lib/syntax_tools/src/syntax_tools.app.src
index dd4ac46055..5c6008a5f0 100644
--- a/lib/syntax_tools/src/syntax_tools.app.src
+++ b/lib/syntax_tools/src/syntax_tools.app.src
@@ -18,4 +18,4 @@
{applications, [stdlib]},
{env, []},
{runtime_dependencies,
- ["compiler-6.0","erts-6.0","kernel-3.0","stdlib-2.5"]}]}.
+ ["compiler-7.0","erts-8.0","kernel-5.0","stdlib-3.0"]}]}.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index eb52cce6af..b935d42bb7 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -61,7 +61,7 @@ appup_test(Config) when is_list(Config) ->
smoke_test(Config) when is_list(Config) ->
Dog = ?t:timetrap(?t:minutes(12)),
Wc = filename:join([code:lib_dir(),"*","src","*.erl"]),
- Fs = filelib:wildcard(Wc),
+ Fs = filelib:wildcard(Wc) ++ test_files(Config),
io:format("~p files\n", [length(Fs)]),
case p_run(fun smoke_test_file/1, Fs) of
0 -> ok;
@@ -93,7 +93,7 @@ print_error_markers(F, File) ->
revert(Config) when is_list(Config) ->
Dog = ?t:timetrap(?t:minutes(12)),
Wc = filename:join([code:lib_dir("stdlib"),"src","*.erl"]),
- Fs = filelib:wildcard(Wc),
+ Fs = filelib:wildcard(Wc) ++ test_files(Config),
Path = [filename:join(code:lib_dir(stdlib), "include"),
filename:join(code:lib_dir(kernel), "include")],
io:format("~p files\n", [length(Fs)]),
@@ -203,18 +203,25 @@ t_erl_parse_type(Config) when is_list(Config) ->
t_epp_dodger(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
- Filenames = ["syntax_tools_SUITE_test_module.erl",
- "syntax_tools_test.erl"],
+ Filenames = test_files(),
ok = test_epp_dodger(Filenames,DataDir,PrivDir),
ok.
t_comment_scan(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
- Filenames = ["syntax_tools_SUITE_test_module.erl",
- "syntax_tools_test.erl"],
+ Filenames = test_files(),
ok = test_comment_scan(Filenames,DataDir),
ok.
+test_files(Config) ->
+ DataDir = ?config(data_dir, Config),
+ [ filename:join(DataDir,Filename) || Filename <- test_files() ].
+
+test_files() ->
+ ["syntax_tools_SUITE_test_module.erl",
+ "syntax_tools_test.erl",
+ "type_specs.erl"].
+
t_igor(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -222,6 +229,12 @@ t_igor(Config) when is_list(Config) ->
FileM2 = filename:join(DataDir,"m2.erl"),
["m.erl",_]=R = igor:merge(m,[FileM1,FileM2],[{outdir,PrivDir}]),
io:format("igor:merge/3 = ~p~n", [R]),
+
+ FileTypeSpecs = filename:join(DataDir,"igor_type_specs.erl"),
+ Empty = filename:join(DataDir,"empty.erl"),
+ ["n.erl",_]=R2 = igor:merge(n,[FileTypeSpecs,Empty],[{outdir,PrivDir}]),
+ io:format("igor:merge/3 = ~p~n", [R2]),
+
ok.
test_comment_scan([],_) -> ok;
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/empty.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/empty.erl
new file mode 100644
index 0000000000..877ff66013
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/empty.erl
@@ -0,0 +1 @@
+-module(empty).
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/igor_type_specs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/igor_type_specs.erl
new file mode 100644
index 0000000000..5a156c7fa3
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/igor_type_specs.erl
@@ -0,0 +1,80 @@
+%% Same as ./type_specs.erl, but without macros.
+-module(igor_type_specs).
+
+-include_lib("syntax_tools/include/merl.hrl").
+
+-export([f/1, b/0, c/2]).
+
+-export_type([t/0, ot/2, ff2/0]).
+
+-type aa() :: _.
+
+-type t() :: integer().
+
+-type ff(A) :: ot(A, A) | tuple() | 1..3 | map() | {}.
+-type ff1() :: ff(bin()) | foo:bar().
+-type ff2() :: {list(), [_], list(integer()),
+ nonempty_list(), nonempty_list(atom()), [ff1(), ...],
+ nil(), []}.
+-type bin() :: <<>>
+ | <<_:(+4)>>
+ | <<_:_*8>>
+ | <<_:12, _:_*16>>
+ | <<_:16, _:_*(0)>> % same as "<<_:16>>"
+ | <<_:16, _:_*(+0)>>.
+
+-callback cb() -> t().
+
+-optional_callbacks([cb/0]).
+
+-opaque ot(A, B) :: {A, B}.
+
+-type f1() :: fun().
+-type f2() :: fun((...) -> t()).
+-type f3() :: fun(() -> t()).
+-type f4() :: fun((t(), t()) -> t()).
+
+-wild(attribute).
+
+-record(par, {a :: undefined | igor_type_specs}).
+
+-record(r0, {}).
+
+-record(r,
+ {f1 :: integer(),
+ f2 = a :: atom(),
+ f3 :: fun(),
+ f4 = 7}).
+
+-type r0() :: #r0{} | #r{f1 :: 3} | #r{f1 :: 3, f2 :: 'sju'}.
+
+-type m1() :: #{}.
+-type m2() :: #{a => m1(), b => #{} | fy:m2()}.
+-type b1() :: B1 :: binary() | (BitString :: bitstring()).
+
+-define(PAIR(A, B), {(A), (B)}).
+
+-spec igor_type_specs:f({r0(), r0()}) -> {t(), t()}.
+
+f({R, R}) ->
+ _ = "igor_type_specs" ++ "hej",
+ _ = <<"foo">>,
+ _ = R#r.f1,
+ _ = R#r{f1 = 17, f2 = b},
+ {1, 1}.
+
+-spec igor_type_specs:b() -> integer() | fun().
+
+b() ->
+ case foo:bar() of
+ #{a := 2} -> 19
+ end.
+
+-spec c(Atom :: atom(), Integer :: integer()) -> {atom(), integer()};
+ (X, Y) -> {atom(), float()} when X :: atom(),
+ is_subtype(Y, float());
+ (integer(), atom()) -> {integer(), atom()}.
+
+c(A, B) ->
+ _ = integer,
+ {A, B}.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl
new file mode 100644
index 0000000000..5621d3a293
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl
@@ -0,0 +1,84 @@
+-module(type_specs).
+
+-include_lib("syntax_tools/include/merl.hrl").
+
+-export([f/1, b/0, c/2]).
+
+-export_type([t/0, ot/2, ff2/0]).
+
+-type aa() :: _.
+
+-type t() :: integer().
+
+-type ff(A) :: ot(A, A) | tuple() | 1..3 | map() | {}.
+-type ff1() :: ff(bin()) | foo:bar().
+-type ff2() :: {list(), [_], list(integer()),
+ nonempty_list(), nonempty_list(atom()), [ff1(), ...],
+ nil(), []}.
+-type bin() :: <<>>
+ | <<_:(+4)>>
+ | <<_:_*8>>
+ | <<_:12, _:_*16>>
+ | <<_:16, _:_*(0)>> % same as "<<_:16>>"
+ | <<_:16, _:_*(+0)>>.
+
+-callback cb() -> t().
+
+-optional_callbacks([cb/0]).
+
+-opaque ot(A, B) :: {A, B}.
+
+-type f1() :: fun().
+-type f2() :: fun((...) -> t()).
+-type f3() :: fun(() -> t()).
+-type f4() :: fun((t(), t()) -> t()).
+
+-wild(attribute).
+
+-record(par, {a :: undefined | ?MODULE}).
+
+-record(r0, {}).
+
+-record(r,
+ {f1 :: integer(),
+ f2 = a :: atom(),
+ f3 :: fun(),
+ f4 = 7}).
+
+-type r0() :: #r0{} | #r{f1 :: 3} | #r{f1 :: 3, f2 :: 'sju'}.
+
+-type m1() :: #{} | map().
+-type m2() :: #{a := m1(), b => #{} | fy:m2()}.
+-type m3() :: #{...}.
+-type m4() :: #{_ => _, ...}.
+-type m5() :: #{any() => any(), ...}. % Currently printed as `#{..., ...}'.
+-type b1() :: B1 :: binary() | (BitString :: bitstring()).
+
+-define(PAIR(A, B), {(A), (B)}).
+
+-spec ?MODULE:f(?PAIR(r0(), r0())) -> ?PAIR(t(), t()).
+
+f({R, R}) ->
+ _ = ?MODULE_STRING ++ "hej",
+ _ = <<"foo">>,
+ _ = R#r.f1,
+ _ = R#r{f1 = 17, f2 = b},
+ {1, 1}.
+
+-spec ?MODULE:b() -> integer() | fun().
+
+b() ->
+ case foo:bar() of
+ #{a := 2} -> 19
+ end.
+
+-define(I, integer).
+
+-spec c(Atom :: atom(), Integer :: ?I()) -> {atom(), integer()};
+ (X, Y) -> {atom(), float()} when X :: atom(),
+ is_subtype(Y, float());
+ (integer(), atom()) -> {integer(), atom()}.
+
+c(A, B) ->
+ _ = ?I,
+ {A, B}.
diff --git a/lib/tools/doc/src/erlang_mode.xml b/lib/tools/doc/src/erlang_mode.xml
index 0ab272fb2f..00cf5196b4 100644
--- a/lib/tools/doc/src/erlang_mode.xml
+++ b/lib/tools/doc/src/erlang_mode.xml
@@ -252,6 +252,7 @@
behavior</item>
<item>gen_event - skeleton for the OTP gen_event behavior</item>
<item>gen_fsm - skeleton for the OTP gen_fsm behavior</item>
+ <item>gen_statem - skeleton for the OTP gen_statem behavior</item>
<item>Library module - skeleton for a module that does not
implement a process.</item>
<item>Corba callback - skeleton for a Corba callback module.</item>
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index 3bb1dda6db..ce26c83295 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -56,6 +56,8 @@
erlang-skel-gen-event erlang-skel-header)
("gen_fsm" "gen-fsm"
erlang-skel-gen-fsm erlang-skel-header)
+ ("gen_statem" "gen-statem"
+ erlang-skel-gen-statem erlang-skel-header)
("wx_object" "wx-object"
erlang-skel-wx-object erlang-skel-header)
("Library module" "gen-lib"
@@ -858,6 +860,122 @@ Please see the function `tempo-define-template'.")
"*The template of a gen_fsm.
Please see the function `tempo-define-template'.")
+(defvar erlang-skel-gen-statem
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_statem)." n n
+
+ "%% API" n
+ "-export([start_link/0])." n
+ n
+ "%% gen_statem callbacks" n
+ "-export([init/1, terminate/3, code_change/4])." n
+ "-export([state_name/3])." n
+ "-export([handle_event/4])." n
+ n
+ "-define(SERVER, ?MODULE)." n
+ n
+ "-record(data, {})." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% API" n
+ (erlang-skel-double-separator-end 3) n
+ (erlang-skel-separator-start 2)
+ "%% @doc" n
+ "%% Creates a gen_statem process which calls Module:init/1 to" n
+ "%% initialize. To ensure a synchronized start-up procedure, this" n
+ "%% function does not return until Module:init/1 has returned." n
+ "%%" n
+ (erlang-skel-separator-end 2)
+ "-spec start_link() ->" n>
+ "{ok, Pid :: pid()} |" n>
+ "ignore |" n>
+ "{error, Error :: term()}." n
+ "start_link() ->" n>
+ "gen_statem:start_link({local, ?SERVER}, ?MODULE, [], [])." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% gen_statem callbacks" n
+ (erlang-skel-double-separator-end 3) n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Whenever a gen_statem is started using gen_statem:start/[3,4] or" n
+ "%% gen_statem:start_link/[3,4], this function is called by the new" n
+ "%% process to initialize." n
+ (erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> " n>
+ "{gen_statem:callback_mode()," n>
+ "State :: term(), Data :: term()} |" n>
+ "{gen_statem:callback_mode()," n>
+ "State :: term(), Data :: term()," n>
+ "[gen_statem:action()] | gen_statem:action()} |" n>
+ "ignore |" n>
+ "{stop, Reason :: term()}." n
+ "init([]) ->" n>
+ "{state_functions, state_name, #data{}}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% If the gen_statem runs with CallbackMode =:= state_functions" n
+ "%% there should be one instance of this function for each possible" n
+ "%% state name. Whenever a gen_statem receives an event," n
+ "%% the instance of this function with the same name" n
+ "%% as the current state name StateName is called to" n
+ "%% handle the event." n
+ (erlang-skel-separator-end 2)
+ "-spec state_name(" n>
+ "gen_statem:event_type(), Msg :: term()," n>
+ "Data :: term()) ->" n>
+ "gen_statem:state_function_result(). " n
+ "state_name({call,Caller}, _Msg, Data) ->" n>
+ "{next_state, state_name, Data, [{reply,Caller,ok}]}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% If the gen_statem runs with CallbackMode =:= handle_event_function" n
+ "%% this function is called for every event a gen_statem receives." n
+ (erlang-skel-separator-end 2)
+ "-spec handle_event(" n>
+ "gen_statem:event_type(), Msg :: term()," n>
+ "State :: term(), Data :: term()) ->" n>
+ "gen_statem:handle_event_result(). " n
+ "handle_event({call,From}, _Msg, State, Data) ->" n>
+ "{next_state, State, Data, [{reply,From,ok}]}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called by a gen_statem when it is about to" n
+ "%% terminate. It should be the opposite of Module:init/1 and do any" n
+ "%% necessary cleaning up. When it returns, the gen_statem terminates with" n
+ "%% Reason. The return value is ignored." n
+ (erlang-skel-separator-end 2)
+ "-spec terminate(Reason :: term(), State :: term(), Data :: term()) ->" n>
+ "any()." n
+ "terminate(_Reason, _State, _Data) ->" n>
+ "void." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Convert process state when code is changed" n
+ (erlang-skel-separator-end 2)
+ "-spec code_change(" n>
+ "OldVsn :: term() | {down,term()}," n>
+ "State :: term(), Data :: term(), Extra :: term()) ->" n>
+ "{ok, NewState :: term(), NewData :: term()}." n
+ "code_change(_OldVsn, State, Data, _Extra) ->" n>
+ "{ok, State, Data}." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% Internal functions" n
+ (erlang-skel-double-separator-end 3)
+ )
+ "*The template of a gen_statem.
+Please see the function `tempo-define-template'.")
+
(defvar erlang-skel-wx-object
'((erlang-skel-include erlang-skel-large-header)
"-behaviour(wx_object)." n n
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 17a156ff00..0a3fc0ddff 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1345,7 +1345,7 @@ Lock syntax table. The effect is that `apply' in the atom
;;;###autoload
-(defun erlang-mode ()
+(define-derived-mode erlang-mode prog-mode "Erlang"
"Major mode for editing Erlang source files in Emacs.
It knows about syntax and comment, it can indent code, it is capable
of fontifying the source file, the TAGS commands are aware of Erlang
@@ -1404,12 +1404,9 @@ and examples of hooks.
Other commands:
\\{erlang-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (setq major-mode 'erlang-mode)
- (setq mode-name "Erlang")
+ ;; Use our own syntax table function
+ :syntax-table nil
(erlang-syntax-table-init)
- (use-local-map erlang-mode-map)
(erlang-electric-init)
(erlang-menu-init)
(erlang-mode-variables)
@@ -1419,12 +1416,8 @@ Other commands:
(erlang-font-lock-init)
(erlang-skel-init)
(tempo-use-tag-list 'erlang-tempo-tags)
- (run-hooks 'erlang-mode-hook)
(if (zerop (buffer-size))
- (run-hooks 'erlang-new-file-hook))
- ;; Doesn't exist in Emacs v21.4; required by Emacs v23.
- (if (boundp 'after-change-major-mode-hook)
- (run-hooks 'after-change-major-mode-hook)))
+ (run-hooks 'erlang-new-file-hook)))
;;;###autoload
(dolist (r '("\\.erl$" "\\.app\\.src$" "\\.escript"
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index f9da748fef..b21eedc625 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1629,15 +1629,24 @@ trace_handler({trace_ts, Pid, in, {_M, _F, Args} = MFArgs, TS} = Trace,
TS;
%%
%% gc_start
-trace_handler({trace_ts, Pid, gc_start, _Func, TS} = Trace,
- Table, _, Dump) ->
+trace_handler({trace_ts, Pid, gc_minor_start, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_start(Table, Pid, TS),
+ TS;
+
+trace_handler({trace_ts, Pid, gc_major_start, _Func, TS} = Trace, Table, _, Dump) ->
dump_stack(Dump, get(Pid), Trace),
trace_gc_start(Table, Pid, TS),
TS;
+
%%
%% gc_end
-trace_handler({trace_ts, Pid, gc_end, _Func, TS} = Trace,
- Table, _, Dump) ->
+trace_handler({trace_ts, Pid, gc_minor_end, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_end(Table, Pid, TS),
+ TS;
+
+trace_handler({trace_ts, Pid, gc_major_end, _Func, TS} = Trace, Table, _, Dump) ->
dump_stack(Dump, get(Pid), Trace),
trace_gc_end(Table, Pid, TS),
TS;
diff --git a/lib/tools/test/fprof_SUITE.erl b/lib/tools/test/fprof_SUITE.erl
index e18d384b52..affb45b7a6 100644
--- a/lib/tools/test/fprof_SUITE.erl
+++ b/lib/tools/test/fprof_SUITE.erl
@@ -949,8 +949,8 @@ handle_trace({trace_ts,Pid,return_to,MFA,TS},P) ->
end,
put({Pid,last_ts},TS),
P;
-handle_trace({trace_ts,Pid,gc_start,_,TS},P) ->
- ?dbg("~p",[{{gc_start,Pid},get(Pid)}]),
+handle_trace({trace_ts,Pid,gc_minor_start,_,TS},P) ->
+ ?dbg("~p",[{{gc_minor_start,Pid},get(Pid)}]),
case get(Pid) of
[suspend|_] = Stack ->
T = ts_sub(TS,get({Pid,last_ts})),
@@ -970,8 +970,40 @@ handle_trace({trace_ts,Pid,gc_start,_,TS},P) ->
end,
put({Pid,last_ts},TS),
P;
-handle_trace({trace_ts,Pid,gc_end,_,TS},P) ->
- ?dbg("~p",[{{gc_end,Pid},get(Pid)}]),
+handle_trace({trace_ts,Pid,gc_major_start,_,TS},P) ->
+ ?dbg("~p",[{{gc_minor_start,Pid},get(Pid)}]),
+ case get(Pid) of
+ [suspend|_] = Stack ->
+ T = ts_sub(TS,get({Pid,last_ts})),
+ insert(Pid,garbage_collect),
+ update_acc(Pid,Stack,T),
+ put(Pid,[garbage_collect|Stack]);
+ [CallingMFA|_] = Stack ->
+ T = ts_sub(TS,get({Pid,last_ts})),
+ insert(Pid,garbage_collect),
+ update_own(Pid,CallingMFA,T),
+ update_acc(Pid,Stack,T),
+ put(Pid,[garbage_collect|Stack]);
+ undefined ->
+ put(first_ts,TS),
+ put(Pid,[garbage_collect]),
+ insert(Pid,garbage_collect)
+ end,
+ put({Pid,last_ts},TS),
+ P;
+handle_trace({trace_ts,Pid,gc_minor_end,_,TS},P) ->
+ ?dbg("~p",[{{gc_minor_end,Pid},get(Pid)}]),
+ T = ts_sub(TS,get({Pid,last_ts})),
+ case get(Pid) of
+ [garbage_collect|RestOfStack] = Stack ->
+ update_own(Pid,garbage_collect,T),
+ update_acc(Pid,Stack,T),
+ put(Pid,RestOfStack)
+ end,
+ put({Pid,last_ts},TS),
+ P;
+handle_trace({trace_ts,Pid,gc_major_end,_,TS},P) ->
+ ?dbg("~p",[{{gc_major_end,Pid},get(Pid)}]),
T = ts_sub(TS,get({Pid,last_ts})),
case get(Pid) of
[garbage_collect|RestOfStack] = Stack ->
diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
index 08fef1c2ff..b9cb4f08cc 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
@@ -43,7 +43,7 @@ case 101: { // wxEvtHandler::Disconnect
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
if(eventType > 0) {
if(recurse_level > 1) {
- delayed_delete->Append(Ecmd.Save());
+ delayed_delete->Append(Ecmd.Save(op));
} else {
bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
(wxObjectEventFunction)(wxEventFunction)
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index 07486e801b..55c179142d 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -195,11 +195,13 @@ gen_funcs(Defs) ->
w("void WxeApp::wxe_dispatch(wxeCommand& Ecmd)~n{~n"),
w(" char * bp = Ecmd.buffer;~n"),
+ w(" int op = Ecmd.op;~n"),
+ w(" Ecmd.op = -1;~n"),
w(" wxeMemEnv *memenv = getMemEnv(Ecmd.port);~n"),
%% w(" wxMBConvUTF32 UTFconverter;~n"),
- w(" wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);~n"),
+ w(" wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);~n"),
w(" try {~n"),
- w(" switch (Ecmd.op)~n{~n"),
+ w(" switch (op)~n{~n"),
%% w(" case WXE_CREATE_PORT:~n", []),
%% w(" { newMemEnv(Ecmd.port); } break;~n", []),
%% w(" case WXE_REMOVE_PORT:~n", []),
@@ -209,7 +211,7 @@ gen_funcs(Defs) ->
w(" wxeRefData *refd = getRefData(This);~n"),
w(" if(This && refd) {~n"),
w(" if(recurse_level > 1 && refd->type != 4) {~n"),
- w(" delayed_delete->Append(Ecmd.Save());~n"),
+ w(" delayed_delete->Append(Ecmd.Save(op));~n"),
w(" } else {~n"),
w(" delete_object(This, refd);~n"),
w(" ((WxeApp *) wxTheApp)->clearPtr(This);}~n"),
@@ -228,7 +230,7 @@ gen_funcs(Defs) ->
w(" default: {~n"),
w(" wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false);"),
w(" error.addAtom(\"_wxe_error_\");~n"),
- w(" error.addInt((int) Ecmd.op);~n"),
+ w(" error.addInt((int) op);~n"),
w(" error.addAtom(\"not_supported\");~n"),
w(" error.addTupleCount(3);~n"),
w(" error.send();~n"),
@@ -239,7 +241,7 @@ gen_funcs(Defs) ->
w("} catch (wxe_badarg badarg) { // try~n"),
w(" wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false);"),
w(" error.addAtom(\"_wxe_error_\");~n"),
- w(" error.addInt((int) Ecmd.op);~n"),
+ w(" error.addInt((int) op);~n"),
w(" error.addAtom(\"badarg\");~n"),
w(" error.addInt((int) badarg.ref);~n"),
w(" error.addTupleCount(2);~n"),
diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf
index 4f7223e147..786e536f93 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -802,9 +802,9 @@
'CanRedo','CanUndo','Copy','Cut','GetInsertionPoint','GetLastPosition',
'GetValue','Paste','Redo','Replace','Remove','SetInsertionPoint',
'SetInsertionPointEnd','SetSelection','SetValue','Undo']}.
-{class, wxGauge, wxControl, [],
- ['wxGauge','~wxGauge','Create','GetBezelFace','GetRange','GetShadowWidth',
- 'GetValue','IsVertical','SetBezelFace','SetRange','SetShadowWidth','SetValue',
+{class, wxGauge, wxControl, [],
+ ['wxGauge','~wxGauge','Create','GetRange',
+ 'GetValue','IsVertical','SetRange','SetValue',
'Pulse']}.
{class, wxGenericDirCtrl, wxControl, [],
['wxGenericDirCtrl','~wxGenericDirCtrl','Create','Init','CollapseTree',
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 059cee59f4..942baf7c7f 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -40,17 +40,19 @@
void WxeApp::wxe_dispatch(wxeCommand& Ecmd)
{
char * bp = Ecmd.buffer;
+ int op = Ecmd.op;
+ Ecmd.op = -1;
wxeMemEnv *memenv = getMemEnv(Ecmd.port);
- wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);
+ wxeReturn rt = wxeReturn(WXE_DRV_PORT, Ecmd.caller, true);
try {
- switch (Ecmd.op)
+ switch (op)
{
case DESTROY_OBJECT: {
void *This = getPtr(bp,memenv);
wxeRefData *refd = getRefData(This);
if(This && refd) {
if(recurse_level > 1 && refd->type != 4) {
- delayed_delete->Append(Ecmd.Save());
+ delayed_delete->Append(Ecmd.Save(op));
} else {
delete_object(This, refd);
((WxeApp *) wxTheApp)->clearPtr(This);}
@@ -114,7 +116,7 @@ case 101: { // wxEvtHandler::Disconnect
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
if(eventType > 0) {
if(recurse_level > 1) {
- delayed_delete->Append(Ecmd.Save());
+ delayed_delete->Append(Ecmd.Save(op));
} else {
bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
(wxObjectEventFunction)(wxEventFunction)
@@ -14628,13 +14630,6 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
rt.addBool(Result);
break;
}
-case wxGauge_GetBezelFace: { // wxGauge::GetBezelFace
- wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
- if(!This) throw wxe_badarg(0);
- int Result = This->GetBezelFace();
- rt.addInt(Result);
- break;
-}
case wxGauge_GetRange: { // wxGauge::GetRange
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
@@ -14642,13 +14637,6 @@ case wxGauge_GetRange: { // wxGauge::GetRange
rt.addInt(Result);
break;
}
-case wxGauge_GetShadowWidth: { // wxGauge::GetShadowWidth
- wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
- if(!This) throw wxe_badarg(0);
- int Result = This->GetShadowWidth();
- rt.addInt(Result);
- break;
-}
case wxGauge_GetValue: { // wxGauge::GetValue
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
@@ -14663,13 +14651,6 @@ case wxGauge_IsVertical: { // wxGauge::IsVertical
rt.addBool(Result);
break;
}
-case wxGauge_SetBezelFace: { // wxGauge::SetBezelFace
- wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
- int * w = (int *) bp; bp += 4;
- if(!This) throw wxe_badarg(0);
- This->SetBezelFace(*w);
- break;
-}
case wxGauge_SetRange: { // wxGauge::SetRange
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
int * r = (int *) bp; bp += 4;
@@ -14677,13 +14658,6 @@ case wxGauge_SetRange: { // wxGauge::SetRange
This->SetRange(*r);
break;
}
-case wxGauge_SetShadowWidth: { // wxGauge::SetShadowWidth
- wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
- int * w = (int *) bp; bp += 4;
- if(!This) throw wxe_badarg(0);
- This->SetShadowWidth(*w);
- break;
-}
case wxGauge_SetValue: { // wxGauge::SetValue
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
@@ -32077,7 +32051,7 @@ case wxDCOverlay_Clear: { // wxDCOverlay::Clear
}
default: {
wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_");
- error.addInt((int) Ecmd.op);
+ error.addInt((int) op);
error.addAtom("not_supported");
error.addTupleCount(3);
error.send();
@@ -32087,7 +32061,7 @@ case wxDCOverlay_Clear: { // wxDCOverlay::Clear
rt.send();
} catch (wxe_badarg badarg) { // try
wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_");
- error.addInt((int) Ecmd.op);
+ error.addInt((int) op);
error.addAtom("badarg");
error.addInt((int) badarg.ref);
error.addTupleCount(2);
diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h
index 0d3b79b7f9..82b39b49cd 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -1506,1924 +1506,1920 @@
#define wxGauge_new_0 1602
#define wxGauge_new_4 1603
#define wxGauge_Create 1604
-#define wxGauge_GetBezelFace 1605
-#define wxGauge_GetRange 1606
-#define wxGauge_GetShadowWidth 1607
-#define wxGauge_GetValue 1608
-#define wxGauge_IsVertical 1609
-#define wxGauge_SetBezelFace 1610
-#define wxGauge_SetRange 1611
-#define wxGauge_SetShadowWidth 1612
-#define wxGauge_SetValue 1613
-#define wxGauge_Pulse 1614
-#define wxGauge_destroy 1615
-#define wxGenericDirCtrl_new_0 1616
-#define wxGenericDirCtrl_new_2 1617
-#define wxGenericDirCtrl_destruct 1618
-#define wxGenericDirCtrl_Create 1619
-#define wxGenericDirCtrl_Init 1620
-#define wxGenericDirCtrl_CollapseTree 1621
-#define wxGenericDirCtrl_ExpandPath 1622
-#define wxGenericDirCtrl_GetDefaultPath 1623
-#define wxGenericDirCtrl_GetPath 1624
-#define wxGenericDirCtrl_GetFilePath 1625
-#define wxGenericDirCtrl_GetFilter 1626
-#define wxGenericDirCtrl_GetFilterIndex 1627
-#define wxGenericDirCtrl_GetRootId 1628
-#define wxGenericDirCtrl_GetTreeCtrl 1629
-#define wxGenericDirCtrl_ReCreateTree 1630
-#define wxGenericDirCtrl_SetDefaultPath 1631
-#define wxGenericDirCtrl_SetFilter 1632
-#define wxGenericDirCtrl_SetFilterIndex 1633
-#define wxGenericDirCtrl_SetPath 1634
-#define wxStaticBox_new_4 1636
-#define wxStaticBox_new_0 1637
-#define wxStaticBox_Create 1638
-#define wxStaticBox_destroy 1639
-#define wxStaticLine_new_2 1641
-#define wxStaticLine_new_0 1642
-#define wxStaticLine_Create 1643
-#define wxStaticLine_IsVertical 1644
-#define wxStaticLine_GetDefaultSize 1645
-#define wxStaticLine_destroy 1646
-#define wxListBox_new_3 1649
-#define wxListBox_new_0 1650
-#define wxListBox_destruct 1652
-#define wxListBox_Create 1654
-#define wxListBox_Deselect 1655
-#define wxListBox_GetSelections 1656
-#define wxListBox_InsertItems 1657
-#define wxListBox_IsSelected 1658
-#define wxListBox_Set 1659
-#define wxListBox_HitTest 1660
-#define wxListBox_SetFirstItem_1_0 1661
-#define wxListBox_SetFirstItem_1_1 1662
-#define wxListCtrl_new_0 1663
-#define wxListCtrl_new_2 1664
-#define wxListCtrl_Arrange 1665
-#define wxListCtrl_AssignImageList 1666
-#define wxListCtrl_ClearAll 1667
-#define wxListCtrl_Create 1668
-#define wxListCtrl_DeleteAllItems 1669
-#define wxListCtrl_DeleteColumn 1670
-#define wxListCtrl_DeleteItem 1671
-#define wxListCtrl_EditLabel 1672
-#define wxListCtrl_EnsureVisible 1673
-#define wxListCtrl_FindItem_3_0 1674
-#define wxListCtrl_FindItem_3_1 1675
-#define wxListCtrl_GetColumn 1676
-#define wxListCtrl_GetColumnCount 1677
-#define wxListCtrl_GetColumnWidth 1678
-#define wxListCtrl_GetCountPerPage 1679
-#define wxListCtrl_GetEditControl 1680
-#define wxListCtrl_GetImageList 1681
-#define wxListCtrl_GetItem 1682
-#define wxListCtrl_GetItemBackgroundColour 1683
-#define wxListCtrl_GetItemCount 1684
-#define wxListCtrl_GetItemData 1685
-#define wxListCtrl_GetItemFont 1686
-#define wxListCtrl_GetItemPosition 1687
-#define wxListCtrl_GetItemRect 1688
-#define wxListCtrl_GetItemSpacing 1689
-#define wxListCtrl_GetItemState 1690
-#define wxListCtrl_GetItemText 1691
-#define wxListCtrl_GetItemTextColour 1692
-#define wxListCtrl_GetNextItem 1693
-#define wxListCtrl_GetSelectedItemCount 1694
-#define wxListCtrl_GetTextColour 1695
-#define wxListCtrl_GetTopItem 1696
-#define wxListCtrl_GetViewRect 1697
-#define wxListCtrl_HitTest 1698
-#define wxListCtrl_InsertColumn_2 1699
-#define wxListCtrl_InsertColumn_3 1700
-#define wxListCtrl_InsertItem_1 1701
-#define wxListCtrl_InsertItem_2_1 1702
-#define wxListCtrl_InsertItem_2_0 1703
-#define wxListCtrl_InsertItem_3 1704
-#define wxListCtrl_RefreshItem 1705
-#define wxListCtrl_RefreshItems 1706
-#define wxListCtrl_ScrollList 1707
-#define wxListCtrl_SetBackgroundColour 1708
-#define wxListCtrl_SetColumn 1709
-#define wxListCtrl_SetColumnWidth 1710
-#define wxListCtrl_SetImageList 1711
-#define wxListCtrl_SetItem_1 1712
-#define wxListCtrl_SetItem_4 1713
-#define wxListCtrl_SetItemBackgroundColour 1714
-#define wxListCtrl_SetItemCount 1715
-#define wxListCtrl_SetItemData 1716
-#define wxListCtrl_SetItemFont 1717
-#define wxListCtrl_SetItemImage 1718
-#define wxListCtrl_SetItemColumnImage 1719
-#define wxListCtrl_SetItemPosition 1720
-#define wxListCtrl_SetItemState 1721
-#define wxListCtrl_SetItemText 1722
-#define wxListCtrl_SetItemTextColour 1723
-#define wxListCtrl_SetSingleStyle 1724
-#define wxListCtrl_SetTextColour 1725
-#define wxListCtrl_SetWindowStyleFlag 1726
-#define wxListCtrl_SortItems 1727
-#define wxListCtrl_destroy 1728
-#define wxListView_ClearColumnImage 1729
-#define wxListView_Focus 1730
-#define wxListView_GetFirstSelected 1731
-#define wxListView_GetFocusedItem 1732
-#define wxListView_GetNextSelected 1733
-#define wxListView_IsSelected 1734
-#define wxListView_Select 1735
-#define wxListView_SetColumnImage 1736
-#define wxListItem_new_0 1737
-#define wxListItem_new_1 1738
-#define wxListItem_destruct 1739
-#define wxListItem_Clear 1740
-#define wxListItem_GetAlign 1741
-#define wxListItem_GetBackgroundColour 1742
-#define wxListItem_GetColumn 1743
-#define wxListItem_GetFont 1744
-#define wxListItem_GetId 1745
-#define wxListItem_GetImage 1746
-#define wxListItem_GetMask 1747
-#define wxListItem_GetState 1748
-#define wxListItem_GetText 1749
-#define wxListItem_GetTextColour 1750
-#define wxListItem_GetWidth 1751
-#define wxListItem_SetAlign 1752
-#define wxListItem_SetBackgroundColour 1753
-#define wxListItem_SetColumn 1754
-#define wxListItem_SetFont 1755
-#define wxListItem_SetId 1756
-#define wxListItem_SetImage 1757
-#define wxListItem_SetMask 1758
-#define wxListItem_SetState 1759
-#define wxListItem_SetStateMask 1760
-#define wxListItem_SetText 1761
-#define wxListItem_SetTextColour 1762
-#define wxListItem_SetWidth 1763
-#define wxListItemAttr_new_0 1764
-#define wxListItemAttr_new_3 1765
-#define wxListItemAttr_GetBackgroundColour 1766
-#define wxListItemAttr_GetFont 1767
-#define wxListItemAttr_GetTextColour 1768
-#define wxListItemAttr_HasBackgroundColour 1769
-#define wxListItemAttr_HasFont 1770
-#define wxListItemAttr_HasTextColour 1771
-#define wxListItemAttr_SetBackgroundColour 1772
-#define wxListItemAttr_SetFont 1773
-#define wxListItemAttr_SetTextColour 1774
-#define wxListItemAttr_destroy 1775
-#define wxImageList_new_0 1776
-#define wxImageList_new_3 1777
-#define wxImageList_Add_1 1778
-#define wxImageList_Add_2_0 1779
-#define wxImageList_Add_2_1 1780
-#define wxImageList_Create 1781
-#define wxImageList_Draw 1783
-#define wxImageList_GetBitmap 1784
-#define wxImageList_GetIcon 1785
-#define wxImageList_GetImageCount 1786
-#define wxImageList_GetSize 1787
-#define wxImageList_Remove 1788
-#define wxImageList_RemoveAll 1789
-#define wxImageList_Replace_2 1790
-#define wxImageList_Replace_3 1791
-#define wxImageList_destroy 1792
-#define wxTextAttr_new_0 1793
-#define wxTextAttr_new_2 1794
-#define wxTextAttr_GetAlignment 1795
-#define wxTextAttr_GetBackgroundColour 1796
-#define wxTextAttr_GetFont 1797
-#define wxTextAttr_GetLeftIndent 1798
-#define wxTextAttr_GetLeftSubIndent 1799
-#define wxTextAttr_GetRightIndent 1800
-#define wxTextAttr_GetTabs 1801
-#define wxTextAttr_GetTextColour 1802
-#define wxTextAttr_HasBackgroundColour 1803
-#define wxTextAttr_HasFont 1804
-#define wxTextAttr_HasTextColour 1805
-#define wxTextAttr_GetFlags 1806
-#define wxTextAttr_IsDefault 1807
-#define wxTextAttr_SetAlignment 1808
-#define wxTextAttr_SetBackgroundColour 1809
-#define wxTextAttr_SetFlags 1810
-#define wxTextAttr_SetFont 1811
-#define wxTextAttr_SetLeftIndent 1812
-#define wxTextAttr_SetRightIndent 1813
-#define wxTextAttr_SetTabs 1814
-#define wxTextAttr_SetTextColour 1815
-#define wxTextAttr_destroy 1816
-#define wxTextCtrl_new_3 1818
-#define wxTextCtrl_new_0 1819
-#define wxTextCtrl_destruct 1821
-#define wxTextCtrl_AppendText 1822
-#define wxTextCtrl_CanCopy 1823
-#define wxTextCtrl_CanCut 1824
-#define wxTextCtrl_CanPaste 1825
-#define wxTextCtrl_CanRedo 1826
-#define wxTextCtrl_CanUndo 1827
-#define wxTextCtrl_Clear 1828
-#define wxTextCtrl_Copy 1829
-#define wxTextCtrl_Create 1830
-#define wxTextCtrl_Cut 1831
-#define wxTextCtrl_DiscardEdits 1832
-#define wxTextCtrl_ChangeValue 1833
-#define wxTextCtrl_EmulateKeyPress 1834
-#define wxTextCtrl_GetDefaultStyle 1835
-#define wxTextCtrl_GetInsertionPoint 1836
-#define wxTextCtrl_GetLastPosition 1837
-#define wxTextCtrl_GetLineLength 1838
-#define wxTextCtrl_GetLineText 1839
-#define wxTextCtrl_GetNumberOfLines 1840
-#define wxTextCtrl_GetRange 1841
-#define wxTextCtrl_GetSelection 1842
-#define wxTextCtrl_GetStringSelection 1843
-#define wxTextCtrl_GetStyle 1844
-#define wxTextCtrl_GetValue 1845
-#define wxTextCtrl_IsEditable 1846
-#define wxTextCtrl_IsModified 1847
-#define wxTextCtrl_IsMultiLine 1848
-#define wxTextCtrl_IsSingleLine 1849
-#define wxTextCtrl_LoadFile 1850
-#define wxTextCtrl_MarkDirty 1851
-#define wxTextCtrl_Paste 1852
-#define wxTextCtrl_PositionToXY 1853
-#define wxTextCtrl_Redo 1854
-#define wxTextCtrl_Remove 1855
-#define wxTextCtrl_Replace 1856
-#define wxTextCtrl_SaveFile 1857
-#define wxTextCtrl_SetDefaultStyle 1858
-#define wxTextCtrl_SetEditable 1859
-#define wxTextCtrl_SetInsertionPoint 1860
-#define wxTextCtrl_SetInsertionPointEnd 1861
-#define wxTextCtrl_SetMaxLength 1863
-#define wxTextCtrl_SetSelection 1864
-#define wxTextCtrl_SetStyle 1865
-#define wxTextCtrl_SetValue 1866
-#define wxTextCtrl_ShowPosition 1867
-#define wxTextCtrl_Undo 1868
-#define wxTextCtrl_WriteText 1869
-#define wxTextCtrl_XYToPosition 1870
-#define wxNotebook_new_0 1873
-#define wxNotebook_new_3 1874
-#define wxNotebook_destruct 1875
-#define wxNotebook_AddPage 1876
-#define wxNotebook_AdvanceSelection 1877
-#define wxNotebook_AssignImageList 1878
-#define wxNotebook_Create 1879
-#define wxNotebook_DeleteAllPages 1880
-#define wxNotebook_DeletePage 1881
-#define wxNotebook_RemovePage 1882
-#define wxNotebook_GetCurrentPage 1883
-#define wxNotebook_GetImageList 1884
-#define wxNotebook_GetPage 1886
-#define wxNotebook_GetPageCount 1887
-#define wxNotebook_GetPageImage 1888
-#define wxNotebook_GetPageText 1889
-#define wxNotebook_GetRowCount 1890
-#define wxNotebook_GetSelection 1891
-#define wxNotebook_GetThemeBackgroundColour 1892
-#define wxNotebook_HitTest 1894
-#define wxNotebook_InsertPage 1896
-#define wxNotebook_SetImageList 1897
-#define wxNotebook_SetPadding 1898
-#define wxNotebook_SetPageSize 1899
-#define wxNotebook_SetPageImage 1900
-#define wxNotebook_SetPageText 1901
-#define wxNotebook_SetSelection 1902
-#define wxNotebook_ChangeSelection 1903
-#define wxChoicebook_new_0 1904
-#define wxChoicebook_new_3 1905
-#define wxChoicebook_AddPage 1906
-#define wxChoicebook_AdvanceSelection 1907
-#define wxChoicebook_AssignImageList 1908
-#define wxChoicebook_Create 1909
-#define wxChoicebook_DeleteAllPages 1910
-#define wxChoicebook_DeletePage 1911
-#define wxChoicebook_RemovePage 1912
-#define wxChoicebook_GetCurrentPage 1913
-#define wxChoicebook_GetImageList 1914
-#define wxChoicebook_GetPage 1916
-#define wxChoicebook_GetPageCount 1917
-#define wxChoicebook_GetPageImage 1918
-#define wxChoicebook_GetPageText 1919
-#define wxChoicebook_GetSelection 1920
-#define wxChoicebook_HitTest 1921
-#define wxChoicebook_InsertPage 1922
-#define wxChoicebook_SetImageList 1923
-#define wxChoicebook_SetPageSize 1924
-#define wxChoicebook_SetPageImage 1925
-#define wxChoicebook_SetPageText 1926
-#define wxChoicebook_SetSelection 1927
-#define wxChoicebook_ChangeSelection 1928
-#define wxChoicebook_destroy 1929
-#define wxToolbook_new_0 1930
-#define wxToolbook_new_3 1931
-#define wxToolbook_AddPage 1932
-#define wxToolbook_AdvanceSelection 1933
-#define wxToolbook_AssignImageList 1934
-#define wxToolbook_Create 1935
-#define wxToolbook_DeleteAllPages 1936
-#define wxToolbook_DeletePage 1937
-#define wxToolbook_RemovePage 1938
-#define wxToolbook_GetCurrentPage 1939
-#define wxToolbook_GetImageList 1940
-#define wxToolbook_GetPage 1942
-#define wxToolbook_GetPageCount 1943
-#define wxToolbook_GetPageImage 1944
-#define wxToolbook_GetPageText 1945
-#define wxToolbook_GetSelection 1946
-#define wxToolbook_HitTest 1948
-#define wxToolbook_InsertPage 1949
-#define wxToolbook_SetImageList 1950
-#define wxToolbook_SetPageSize 1951
-#define wxToolbook_SetPageImage 1952
-#define wxToolbook_SetPageText 1953
-#define wxToolbook_SetSelection 1954
-#define wxToolbook_ChangeSelection 1955
-#define wxToolbook_destroy 1956
-#define wxListbook_new_0 1957
-#define wxListbook_new_3 1958
-#define wxListbook_AddPage 1959
-#define wxListbook_AdvanceSelection 1960
-#define wxListbook_AssignImageList 1961
-#define wxListbook_Create 1962
-#define wxListbook_DeleteAllPages 1963
-#define wxListbook_DeletePage 1964
-#define wxListbook_RemovePage 1965
-#define wxListbook_GetCurrentPage 1966
-#define wxListbook_GetImageList 1967
-#define wxListbook_GetPage 1969
-#define wxListbook_GetPageCount 1970
-#define wxListbook_GetPageImage 1971
-#define wxListbook_GetPageText 1972
-#define wxListbook_GetSelection 1973
-#define wxListbook_HitTest 1975
-#define wxListbook_InsertPage 1976
-#define wxListbook_SetImageList 1977
-#define wxListbook_SetPageSize 1978
-#define wxListbook_SetPageImage 1979
-#define wxListbook_SetPageText 1980
-#define wxListbook_SetSelection 1981
-#define wxListbook_ChangeSelection 1982
-#define wxListbook_destroy 1983
-#define wxTreebook_new_0 1984
-#define wxTreebook_new_3 1985
-#define wxTreebook_AddPage 1986
-#define wxTreebook_AdvanceSelection 1987
-#define wxTreebook_AssignImageList 1988
-#define wxTreebook_Create 1989
-#define wxTreebook_DeleteAllPages 1990
-#define wxTreebook_DeletePage 1991
-#define wxTreebook_RemovePage 1992
-#define wxTreebook_GetCurrentPage 1993
-#define wxTreebook_GetImageList 1994
-#define wxTreebook_GetPage 1996
-#define wxTreebook_GetPageCount 1997
-#define wxTreebook_GetPageImage 1998
-#define wxTreebook_GetPageText 1999
-#define wxTreebook_GetSelection 2000
-#define wxTreebook_ExpandNode 2001
-#define wxTreebook_IsNodeExpanded 2002
-#define wxTreebook_HitTest 2004
-#define wxTreebook_InsertPage 2005
-#define wxTreebook_InsertSubPage 2006
-#define wxTreebook_SetImageList 2007
-#define wxTreebook_SetPageSize 2008
-#define wxTreebook_SetPageImage 2009
-#define wxTreebook_SetPageText 2010
-#define wxTreebook_SetSelection 2011
-#define wxTreebook_ChangeSelection 2012
-#define wxTreebook_destroy 2013
-#define wxTreeCtrl_new_2 2016
-#define wxTreeCtrl_new_0 2017
-#define wxTreeCtrl_destruct 2019
-#define wxTreeCtrl_AddRoot 2020
-#define wxTreeCtrl_AppendItem 2021
-#define wxTreeCtrl_AssignImageList 2022
-#define wxTreeCtrl_AssignStateImageList 2023
-#define wxTreeCtrl_Collapse 2024
-#define wxTreeCtrl_CollapseAndReset 2025
-#define wxTreeCtrl_Create 2026
-#define wxTreeCtrl_Delete 2027
-#define wxTreeCtrl_DeleteAllItems 2028
-#define wxTreeCtrl_DeleteChildren 2029
-#define wxTreeCtrl_EditLabel 2030
-#define wxTreeCtrl_EnsureVisible 2031
-#define wxTreeCtrl_Expand 2032
-#define wxTreeCtrl_GetBoundingRect 2033
-#define wxTreeCtrl_GetChildrenCount 2035
-#define wxTreeCtrl_GetCount 2036
-#define wxTreeCtrl_GetEditControl 2037
-#define wxTreeCtrl_GetFirstChild 2038
-#define wxTreeCtrl_GetNextChild 2039
-#define wxTreeCtrl_GetFirstVisibleItem 2040
-#define wxTreeCtrl_GetImageList 2041
-#define wxTreeCtrl_GetIndent 2042
-#define wxTreeCtrl_GetItemBackgroundColour 2043
-#define wxTreeCtrl_GetItemData 2044
-#define wxTreeCtrl_GetItemFont 2045
-#define wxTreeCtrl_GetItemImage_1 2046
-#define wxTreeCtrl_GetItemImage_2 2047
-#define wxTreeCtrl_GetItemText 2048
-#define wxTreeCtrl_GetItemTextColour 2049
-#define wxTreeCtrl_GetLastChild 2050
-#define wxTreeCtrl_GetNextSibling 2051
-#define wxTreeCtrl_GetNextVisible 2052
-#define wxTreeCtrl_GetItemParent 2053
-#define wxTreeCtrl_GetPrevSibling 2054
-#define wxTreeCtrl_GetPrevVisible 2055
-#define wxTreeCtrl_GetRootItem 2056
-#define wxTreeCtrl_GetSelection 2057
-#define wxTreeCtrl_GetSelections 2058
-#define wxTreeCtrl_GetStateImageList 2059
-#define wxTreeCtrl_HitTest 2060
-#define wxTreeCtrl_InsertItem 2062
-#define wxTreeCtrl_IsBold 2063
-#define wxTreeCtrl_IsExpanded 2064
-#define wxTreeCtrl_IsSelected 2065
-#define wxTreeCtrl_IsVisible 2066
-#define wxTreeCtrl_ItemHasChildren 2067
-#define wxTreeCtrl_IsTreeItemIdOk 2068
-#define wxTreeCtrl_PrependItem 2069
-#define wxTreeCtrl_ScrollTo 2070
-#define wxTreeCtrl_SelectItem_1 2071
-#define wxTreeCtrl_SelectItem_2 2072
-#define wxTreeCtrl_SetIndent 2073
-#define wxTreeCtrl_SetImageList 2074
-#define wxTreeCtrl_SetItemBackgroundColour 2075
-#define wxTreeCtrl_SetItemBold 2076
-#define wxTreeCtrl_SetItemData 2077
-#define wxTreeCtrl_SetItemDropHighlight 2078
-#define wxTreeCtrl_SetItemFont 2079
-#define wxTreeCtrl_SetItemHasChildren 2080
-#define wxTreeCtrl_SetItemImage_2 2081
-#define wxTreeCtrl_SetItemImage_3 2082
-#define wxTreeCtrl_SetItemText 2083
-#define wxTreeCtrl_SetItemTextColour 2084
-#define wxTreeCtrl_SetStateImageList 2085
-#define wxTreeCtrl_SetWindowStyle 2086
-#define wxTreeCtrl_SortChildren 2087
-#define wxTreeCtrl_Toggle 2088
-#define wxTreeCtrl_ToggleItemSelection 2089
-#define wxTreeCtrl_Unselect 2090
-#define wxTreeCtrl_UnselectAll 2091
-#define wxTreeCtrl_UnselectItem 2092
-#define wxScrollBar_new_0 2093
-#define wxScrollBar_new_3 2094
-#define wxScrollBar_destruct 2095
-#define wxScrollBar_Create 2096
-#define wxScrollBar_GetRange 2097
-#define wxScrollBar_GetPageSize 2098
-#define wxScrollBar_GetThumbPosition 2099
-#define wxScrollBar_GetThumbSize 2100
-#define wxScrollBar_SetThumbPosition 2101
-#define wxScrollBar_SetScrollbar 2102
-#define wxSpinButton_new_2 2104
-#define wxSpinButton_new_0 2105
-#define wxSpinButton_Create 2106
-#define wxSpinButton_GetMax 2107
-#define wxSpinButton_GetMin 2108
-#define wxSpinButton_GetValue 2109
-#define wxSpinButton_SetRange 2110
-#define wxSpinButton_SetValue 2111
-#define wxSpinButton_destroy 2112
-#define wxSpinCtrl_new_0 2113
-#define wxSpinCtrl_new_2 2114
-#define wxSpinCtrl_Create 2116
-#define wxSpinCtrl_SetValue_1_1 2119
-#define wxSpinCtrl_SetValue_1_0 2120
-#define wxSpinCtrl_GetValue 2122
-#define wxSpinCtrl_SetRange 2124
-#define wxSpinCtrl_SetSelection 2125
-#define wxSpinCtrl_GetMin 2127
-#define wxSpinCtrl_GetMax 2129
-#define wxSpinCtrl_destroy 2130
-#define wxStaticText_new_0 2131
-#define wxStaticText_new_4 2132
-#define wxStaticText_Create 2133
-#define wxStaticText_GetLabel 2134
-#define wxStaticText_SetLabel 2135
-#define wxStaticText_Wrap 2136
-#define wxStaticText_destroy 2137
-#define wxStaticBitmap_new_0 2138
-#define wxStaticBitmap_new_4 2139
-#define wxStaticBitmap_Create 2140
-#define wxStaticBitmap_GetBitmap 2141
-#define wxStaticBitmap_SetBitmap 2142
-#define wxStaticBitmap_destroy 2143
-#define wxRadioBox_new 2144
-#define wxRadioBox_destruct 2146
-#define wxRadioBox_Create 2147
-#define wxRadioBox_Enable_2 2148
-#define wxRadioBox_Enable_1 2149
-#define wxRadioBox_GetSelection 2150
-#define wxRadioBox_GetString 2151
-#define wxRadioBox_SetSelection 2152
-#define wxRadioBox_Show_2 2153
-#define wxRadioBox_Show_1 2154
-#define wxRadioBox_GetColumnCount 2155
-#define wxRadioBox_GetItemHelpText 2156
-#define wxRadioBox_GetItemToolTip 2157
-#define wxRadioBox_GetItemFromPoint 2159
-#define wxRadioBox_GetRowCount 2160
-#define wxRadioBox_IsItemEnabled 2161
-#define wxRadioBox_IsItemShown 2162
-#define wxRadioBox_SetItemHelpText 2163
-#define wxRadioBox_SetItemToolTip 2164
-#define wxRadioButton_new_0 2165
-#define wxRadioButton_new_4 2166
-#define wxRadioButton_Create 2167
-#define wxRadioButton_GetValue 2168
-#define wxRadioButton_SetValue 2169
-#define wxRadioButton_destroy 2170
-#define wxSlider_new_6 2172
-#define wxSlider_new_0 2173
-#define wxSlider_Create 2174
-#define wxSlider_GetLineSize 2175
-#define wxSlider_GetMax 2176
-#define wxSlider_GetMin 2177
-#define wxSlider_GetPageSize 2178
-#define wxSlider_GetThumbLength 2179
-#define wxSlider_GetValue 2180
-#define wxSlider_SetLineSize 2181
-#define wxSlider_SetPageSize 2182
-#define wxSlider_SetRange 2183
-#define wxSlider_SetThumbLength 2184
-#define wxSlider_SetValue 2185
-#define wxSlider_destroy 2186
-#define wxDialog_new_4 2188
-#define wxDialog_new_0 2189
-#define wxDialog_destruct 2191
-#define wxDialog_Create 2192
-#define wxDialog_CreateButtonSizer 2193
-#define wxDialog_CreateStdDialogButtonSizer 2194
-#define wxDialog_EndModal 2195
-#define wxDialog_GetAffirmativeId 2196
-#define wxDialog_GetReturnCode 2197
-#define wxDialog_IsModal 2198
-#define wxDialog_SetAffirmativeId 2199
-#define wxDialog_SetReturnCode 2200
-#define wxDialog_Show 2201
-#define wxDialog_ShowModal 2202
-#define wxColourDialog_new_0 2203
-#define wxColourDialog_new_2 2204
-#define wxColourDialog_destruct 2205
-#define wxColourDialog_Create 2206
-#define wxColourDialog_GetColourData 2207
-#define wxColourData_new_0 2208
-#define wxColourData_new_1 2209
-#define wxColourData_destruct 2210
-#define wxColourData_GetChooseFull 2211
-#define wxColourData_GetColour 2212
-#define wxColourData_GetCustomColour 2214
-#define wxColourData_SetChooseFull 2215
-#define wxColourData_SetColour 2216
-#define wxColourData_SetCustomColour 2217
-#define wxPalette_new_0 2218
-#define wxPalette_new_4 2219
-#define wxPalette_destruct 2221
-#define wxPalette_Create 2222
-#define wxPalette_GetColoursCount 2223
-#define wxPalette_GetPixel 2224
-#define wxPalette_GetRGB 2225
-#define wxPalette_IsOk 2226
-#define wxDirDialog_new 2230
-#define wxDirDialog_destruct 2231
-#define wxDirDialog_GetPath 2232
-#define wxDirDialog_GetMessage 2233
-#define wxDirDialog_SetMessage 2234
-#define wxDirDialog_SetPath 2235
-#define wxFileDialog_new 2239
-#define wxFileDialog_destruct 2240
-#define wxFileDialog_GetDirectory 2241
-#define wxFileDialog_GetFilename 2242
-#define wxFileDialog_GetFilenames 2243
-#define wxFileDialog_GetFilterIndex 2244
-#define wxFileDialog_GetMessage 2245
-#define wxFileDialog_GetPath 2246
-#define wxFileDialog_GetPaths 2247
-#define wxFileDialog_GetWildcard 2248
-#define wxFileDialog_SetDirectory 2249
-#define wxFileDialog_SetFilename 2250
-#define wxFileDialog_SetFilterIndex 2251
-#define wxFileDialog_SetMessage 2252
-#define wxFileDialog_SetPath 2253
-#define wxFileDialog_SetWildcard 2254
-#define wxPickerBase_SetInternalMargin 2255
-#define wxPickerBase_GetInternalMargin 2256
-#define wxPickerBase_SetTextCtrlProportion 2257
-#define wxPickerBase_SetPickerCtrlProportion 2258
-#define wxPickerBase_GetTextCtrlProportion 2259
-#define wxPickerBase_GetPickerCtrlProportion 2260
-#define wxPickerBase_HasTextCtrl 2261
-#define wxPickerBase_GetTextCtrl 2262
-#define wxPickerBase_IsTextCtrlGrowable 2263
-#define wxPickerBase_SetPickerCtrlGrowable 2264
-#define wxPickerBase_SetTextCtrlGrowable 2265
-#define wxPickerBase_IsPickerCtrlGrowable 2266
-#define wxFilePickerCtrl_new_0 2267
-#define wxFilePickerCtrl_new_3 2268
-#define wxFilePickerCtrl_Create 2269
-#define wxFilePickerCtrl_GetPath 2270
-#define wxFilePickerCtrl_SetPath 2271
-#define wxFilePickerCtrl_destroy 2272
-#define wxDirPickerCtrl_new_0 2273
-#define wxDirPickerCtrl_new_3 2274
-#define wxDirPickerCtrl_Create 2275
-#define wxDirPickerCtrl_GetPath 2276
-#define wxDirPickerCtrl_SetPath 2277
-#define wxDirPickerCtrl_destroy 2278
-#define wxColourPickerCtrl_new_0 2279
-#define wxColourPickerCtrl_new_3 2280
-#define wxColourPickerCtrl_Create 2281
-#define wxColourPickerCtrl_GetColour 2282
-#define wxColourPickerCtrl_SetColour_1_1 2283
-#define wxColourPickerCtrl_SetColour_1_0 2284
-#define wxColourPickerCtrl_destroy 2285
-#define wxDatePickerCtrl_new_0 2286
-#define wxDatePickerCtrl_new_3 2287
-#define wxDatePickerCtrl_GetRange 2288
-#define wxDatePickerCtrl_GetValue 2289
-#define wxDatePickerCtrl_SetRange 2290
-#define wxDatePickerCtrl_SetValue 2291
-#define wxDatePickerCtrl_destroy 2292
-#define wxFontPickerCtrl_new_0 2293
-#define wxFontPickerCtrl_new_3 2294
-#define wxFontPickerCtrl_Create 2295
-#define wxFontPickerCtrl_GetSelectedFont 2296
-#define wxFontPickerCtrl_SetSelectedFont 2297
-#define wxFontPickerCtrl_GetMaxPointSize 2298
-#define wxFontPickerCtrl_SetMaxPointSize 2299
-#define wxFontPickerCtrl_destroy 2300
-#define wxFindReplaceDialog_new_0 2303
-#define wxFindReplaceDialog_new_4 2304
-#define wxFindReplaceDialog_destruct 2305
-#define wxFindReplaceDialog_Create 2306
-#define wxFindReplaceDialog_GetData 2307
-#define wxFindReplaceData_new_0 2308
-#define wxFindReplaceData_new_1 2309
-#define wxFindReplaceData_GetFindString 2310
-#define wxFindReplaceData_GetReplaceString 2311
-#define wxFindReplaceData_GetFlags 2312
-#define wxFindReplaceData_SetFlags 2313
-#define wxFindReplaceData_SetFindString 2314
-#define wxFindReplaceData_SetReplaceString 2315
-#define wxFindReplaceData_destroy 2316
-#define wxMultiChoiceDialog_new_0 2317
-#define wxMultiChoiceDialog_new_5 2319
-#define wxMultiChoiceDialog_GetSelections 2320
-#define wxMultiChoiceDialog_SetSelections 2321
-#define wxMultiChoiceDialog_destroy 2322
-#define wxSingleChoiceDialog_new_0 2323
-#define wxSingleChoiceDialog_new_5 2325
-#define wxSingleChoiceDialog_GetSelection 2326
-#define wxSingleChoiceDialog_GetStringSelection 2327
-#define wxSingleChoiceDialog_SetSelection 2328
-#define wxSingleChoiceDialog_destroy 2329
-#define wxTextEntryDialog_new 2330
-#define wxTextEntryDialog_GetValue 2331
-#define wxTextEntryDialog_SetValue 2332
-#define wxTextEntryDialog_destroy 2333
-#define wxPasswordEntryDialog_new 2334
-#define wxPasswordEntryDialog_destroy 2335
-#define wxFontData_new_0 2336
-#define wxFontData_new_1 2337
-#define wxFontData_destruct 2338
-#define wxFontData_EnableEffects 2339
-#define wxFontData_GetAllowSymbols 2340
-#define wxFontData_GetColour 2341
-#define wxFontData_GetChosenFont 2342
-#define wxFontData_GetEnableEffects 2343
-#define wxFontData_GetInitialFont 2344
-#define wxFontData_GetShowHelp 2345
-#define wxFontData_SetAllowSymbols 2346
-#define wxFontData_SetChosenFont 2347
-#define wxFontData_SetColour 2348
-#define wxFontData_SetInitialFont 2349
-#define wxFontData_SetRange 2350
-#define wxFontData_SetShowHelp 2351
-#define wxFontDialog_new_0 2355
-#define wxFontDialog_new_2 2357
-#define wxFontDialog_Create 2359
-#define wxFontDialog_GetFontData 2360
-#define wxFontDialog_destroy 2362
-#define wxProgressDialog_new 2363
-#define wxProgressDialog_destruct 2364
-#define wxProgressDialog_Resume 2365
-#define wxProgressDialog_Update_2 2366
-#define wxProgressDialog_Update_0 2367
-#define wxMessageDialog_new 2368
-#define wxMessageDialog_destruct 2369
-#define wxPageSetupDialog_new 2370
-#define wxPageSetupDialog_destruct 2371
-#define wxPageSetupDialog_GetPageSetupData 2372
-#define wxPageSetupDialog_ShowModal 2373
-#define wxPageSetupDialogData_new_0 2374
-#define wxPageSetupDialogData_new_1_0 2375
-#define wxPageSetupDialogData_new_1_1 2376
-#define wxPageSetupDialogData_destruct 2377
-#define wxPageSetupDialogData_EnableHelp 2378
-#define wxPageSetupDialogData_EnableMargins 2379
-#define wxPageSetupDialogData_EnableOrientation 2380
-#define wxPageSetupDialogData_EnablePaper 2381
-#define wxPageSetupDialogData_EnablePrinter 2382
-#define wxPageSetupDialogData_GetDefaultMinMargins 2383
-#define wxPageSetupDialogData_GetEnableMargins 2384
-#define wxPageSetupDialogData_GetEnableOrientation 2385
-#define wxPageSetupDialogData_GetEnablePaper 2386
-#define wxPageSetupDialogData_GetEnablePrinter 2387
-#define wxPageSetupDialogData_GetEnableHelp 2388
-#define wxPageSetupDialogData_GetDefaultInfo 2389
-#define wxPageSetupDialogData_GetMarginTopLeft 2390
-#define wxPageSetupDialogData_GetMarginBottomRight 2391
-#define wxPageSetupDialogData_GetMinMarginTopLeft 2392
-#define wxPageSetupDialogData_GetMinMarginBottomRight 2393
-#define wxPageSetupDialogData_GetPaperId 2394
-#define wxPageSetupDialogData_GetPaperSize 2395
-#define wxPageSetupDialogData_GetPrintData 2397
-#define wxPageSetupDialogData_IsOk 2398
-#define wxPageSetupDialogData_SetDefaultInfo 2399
-#define wxPageSetupDialogData_SetDefaultMinMargins 2400
-#define wxPageSetupDialogData_SetMarginTopLeft 2401
-#define wxPageSetupDialogData_SetMarginBottomRight 2402
-#define wxPageSetupDialogData_SetMinMarginTopLeft 2403
-#define wxPageSetupDialogData_SetMinMarginBottomRight 2404
-#define wxPageSetupDialogData_SetPaperId 2405
-#define wxPageSetupDialogData_SetPaperSize_1_1 2406
-#define wxPageSetupDialogData_SetPaperSize_1_0 2407
-#define wxPageSetupDialogData_SetPrintData 2408
-#define wxPrintDialog_new_2_0 2409
-#define wxPrintDialog_new_2_1 2410
-#define wxPrintDialog_destruct 2411
-#define wxPrintDialog_GetPrintDialogData 2412
-#define wxPrintDialog_GetPrintDC 2413
-#define wxPrintDialogData_new_0 2414
-#define wxPrintDialogData_new_1_1 2415
-#define wxPrintDialogData_new_1_0 2416
-#define wxPrintDialogData_destruct 2417
-#define wxPrintDialogData_EnableHelp 2418
-#define wxPrintDialogData_EnablePageNumbers 2419
-#define wxPrintDialogData_EnablePrintToFile 2420
-#define wxPrintDialogData_EnableSelection 2421
-#define wxPrintDialogData_GetAllPages 2422
-#define wxPrintDialogData_GetCollate 2423
-#define wxPrintDialogData_GetFromPage 2424
-#define wxPrintDialogData_GetMaxPage 2425
-#define wxPrintDialogData_GetMinPage 2426
-#define wxPrintDialogData_GetNoCopies 2427
-#define wxPrintDialogData_GetPrintData 2428
-#define wxPrintDialogData_GetPrintToFile 2429
-#define wxPrintDialogData_GetSelection 2430
-#define wxPrintDialogData_GetToPage 2431
-#define wxPrintDialogData_IsOk 2432
-#define wxPrintDialogData_SetCollate 2433
-#define wxPrintDialogData_SetFromPage 2434
-#define wxPrintDialogData_SetMaxPage 2435
-#define wxPrintDialogData_SetMinPage 2436
-#define wxPrintDialogData_SetNoCopies 2437
-#define wxPrintDialogData_SetPrintData 2438
-#define wxPrintDialogData_SetPrintToFile 2439
-#define wxPrintDialogData_SetSelection 2440
-#define wxPrintDialogData_SetToPage 2441
-#define wxPrintData_new_0 2442
-#define wxPrintData_new_1 2443
-#define wxPrintData_destruct 2444
-#define wxPrintData_GetCollate 2445
-#define wxPrintData_GetBin 2446
-#define wxPrintData_GetColour 2447
-#define wxPrintData_GetDuplex 2448
-#define wxPrintData_GetNoCopies 2449
-#define wxPrintData_GetOrientation 2450
-#define wxPrintData_GetPaperId 2451
-#define wxPrintData_GetPrinterName 2452
-#define wxPrintData_GetQuality 2453
-#define wxPrintData_IsOk 2454
-#define wxPrintData_SetBin 2455
-#define wxPrintData_SetCollate 2456
-#define wxPrintData_SetColour 2457
-#define wxPrintData_SetDuplex 2458
-#define wxPrintData_SetNoCopies 2459
-#define wxPrintData_SetOrientation 2460
-#define wxPrintData_SetPaperId 2461
-#define wxPrintData_SetPrinterName 2462
-#define wxPrintData_SetQuality 2463
-#define wxPrintPreview_new_2 2466
-#define wxPrintPreview_new_3 2467
-#define wxPrintPreview_destruct 2469
-#define wxPrintPreview_GetCanvas 2470
-#define wxPrintPreview_GetCurrentPage 2471
-#define wxPrintPreview_GetFrame 2472
-#define wxPrintPreview_GetMaxPage 2473
-#define wxPrintPreview_GetMinPage 2474
-#define wxPrintPreview_GetPrintout 2475
-#define wxPrintPreview_GetPrintoutForPrinting 2476
-#define wxPrintPreview_IsOk 2477
-#define wxPrintPreview_PaintPage 2478
-#define wxPrintPreview_Print 2479
-#define wxPrintPreview_RenderPage 2480
-#define wxPrintPreview_SetCanvas 2481
-#define wxPrintPreview_SetCurrentPage 2482
-#define wxPrintPreview_SetFrame 2483
-#define wxPrintPreview_SetPrintout 2484
-#define wxPrintPreview_SetZoom 2485
-#define wxPreviewFrame_new 2486
-#define wxPreviewFrame_destruct 2487
-#define wxPreviewFrame_CreateControlBar 2488
-#define wxPreviewFrame_CreateCanvas 2489
-#define wxPreviewFrame_Initialize 2490
-#define wxPreviewFrame_OnCloseWindow 2491
-#define wxPreviewControlBar_new 2492
-#define wxPreviewControlBar_destruct 2493
-#define wxPreviewControlBar_CreateButtons 2494
-#define wxPreviewControlBar_GetPrintPreview 2495
-#define wxPreviewControlBar_GetZoomControl 2496
-#define wxPreviewControlBar_SetZoomControl 2497
-#define wxPrinter_new 2499
-#define wxPrinter_CreateAbortWindow 2500
-#define wxPrinter_GetAbort 2501
-#define wxPrinter_GetLastError 2502
-#define wxPrinter_GetPrintDialogData 2503
-#define wxPrinter_Print 2504
-#define wxPrinter_PrintDialog 2505
-#define wxPrinter_ReportError 2506
-#define wxPrinter_Setup 2507
-#define wxPrinter_destroy 2508
-#define wxXmlResource_new_1 2509
-#define wxXmlResource_new_2 2510
-#define wxXmlResource_destruct 2511
-#define wxXmlResource_AttachUnknownControl 2512
-#define wxXmlResource_ClearHandlers 2513
-#define wxXmlResource_CompareVersion 2514
-#define wxXmlResource_Get 2515
-#define wxXmlResource_GetFlags 2516
-#define wxXmlResource_GetVersion 2517
-#define wxXmlResource_GetXRCID 2518
-#define wxXmlResource_InitAllHandlers 2519
-#define wxXmlResource_Load 2520
-#define wxXmlResource_LoadBitmap 2521
-#define wxXmlResource_LoadDialog_2 2522
-#define wxXmlResource_LoadDialog_3 2523
-#define wxXmlResource_LoadFrame_2 2524
-#define wxXmlResource_LoadFrame_3 2525
-#define wxXmlResource_LoadIcon 2526
-#define wxXmlResource_LoadMenu 2527
-#define wxXmlResource_LoadMenuBar_2 2528
-#define wxXmlResource_LoadMenuBar_1 2529
-#define wxXmlResource_LoadPanel_2 2530
-#define wxXmlResource_LoadPanel_3 2531
-#define wxXmlResource_LoadToolBar 2532
-#define wxXmlResource_Set 2533
-#define wxXmlResource_SetFlags 2534
-#define wxXmlResource_Unload 2535
-#define wxXmlResource_xrcctrl 2536
-#define wxHtmlEasyPrinting_new 2537
-#define wxHtmlEasyPrinting_destruct 2538
-#define wxHtmlEasyPrinting_GetPrintData 2539
-#define wxHtmlEasyPrinting_GetPageSetupData 2540
-#define wxHtmlEasyPrinting_PreviewFile 2541
-#define wxHtmlEasyPrinting_PreviewText 2542
-#define wxHtmlEasyPrinting_PrintFile 2543
-#define wxHtmlEasyPrinting_PrintText 2544
-#define wxHtmlEasyPrinting_PageSetup 2545
-#define wxHtmlEasyPrinting_SetFonts 2546
-#define wxHtmlEasyPrinting_SetHeader 2547
-#define wxHtmlEasyPrinting_SetFooter 2548
-#define wxGLCanvas_new_2 2550
-#define wxGLCanvas_new_3_1 2551
-#define wxGLCanvas_new_3_0 2552
-#define wxGLCanvas_GetContext 2553
-#define wxGLCanvas_SetCurrent 2555
-#define wxGLCanvas_SwapBuffers 2556
-#define wxGLCanvas_destroy 2557
-#define wxAuiManager_new 2558
-#define wxAuiManager_destruct 2559
-#define wxAuiManager_AddPane_2_1 2560
-#define wxAuiManager_AddPane_3 2561
-#define wxAuiManager_AddPane_2_0 2562
-#define wxAuiManager_DetachPane 2563
-#define wxAuiManager_GetAllPanes 2564
-#define wxAuiManager_GetArtProvider 2565
-#define wxAuiManager_GetDockSizeConstraint 2566
-#define wxAuiManager_GetFlags 2567
-#define wxAuiManager_GetManagedWindow 2568
-#define wxAuiManager_GetManager 2569
-#define wxAuiManager_GetPane_1_1 2570
-#define wxAuiManager_GetPane_1_0 2571
-#define wxAuiManager_HideHint 2572
-#define wxAuiManager_InsertPane 2573
-#define wxAuiManager_LoadPaneInfo 2574
-#define wxAuiManager_LoadPerspective 2575
-#define wxAuiManager_SavePaneInfo 2576
-#define wxAuiManager_SavePerspective 2577
-#define wxAuiManager_SetArtProvider 2578
-#define wxAuiManager_SetDockSizeConstraint 2579
-#define wxAuiManager_SetFlags 2580
-#define wxAuiManager_SetManagedWindow 2581
-#define wxAuiManager_ShowHint 2582
-#define wxAuiManager_UnInit 2583
-#define wxAuiManager_Update 2584
-#define wxAuiPaneInfo_new_0 2585
-#define wxAuiPaneInfo_new_1 2586
-#define wxAuiPaneInfo_destruct 2587
-#define wxAuiPaneInfo_BestSize_1 2588
-#define wxAuiPaneInfo_BestSize_2 2589
-#define wxAuiPaneInfo_Bottom 2590
-#define wxAuiPaneInfo_BottomDockable 2591
-#define wxAuiPaneInfo_Caption 2592
-#define wxAuiPaneInfo_CaptionVisible 2593
-#define wxAuiPaneInfo_Centre 2594
-#define wxAuiPaneInfo_CentrePane 2595
-#define wxAuiPaneInfo_CloseButton 2596
-#define wxAuiPaneInfo_DefaultPane 2597
-#define wxAuiPaneInfo_DestroyOnClose 2598
-#define wxAuiPaneInfo_Direction 2599
-#define wxAuiPaneInfo_Dock 2600
-#define wxAuiPaneInfo_Dockable 2601
-#define wxAuiPaneInfo_Fixed 2602
-#define wxAuiPaneInfo_Float 2603
-#define wxAuiPaneInfo_Floatable 2604
-#define wxAuiPaneInfo_FloatingPosition_1 2605
-#define wxAuiPaneInfo_FloatingPosition_2 2606
-#define wxAuiPaneInfo_FloatingSize_1 2607
-#define wxAuiPaneInfo_FloatingSize_2 2608
-#define wxAuiPaneInfo_Gripper 2609
-#define wxAuiPaneInfo_GripperTop 2610
-#define wxAuiPaneInfo_HasBorder 2611
-#define wxAuiPaneInfo_HasCaption 2612
-#define wxAuiPaneInfo_HasCloseButton 2613
-#define wxAuiPaneInfo_HasFlag 2614
-#define wxAuiPaneInfo_HasGripper 2615
-#define wxAuiPaneInfo_HasGripperTop 2616
-#define wxAuiPaneInfo_HasMaximizeButton 2617
-#define wxAuiPaneInfo_HasMinimizeButton 2618
-#define wxAuiPaneInfo_HasPinButton 2619
-#define wxAuiPaneInfo_Hide 2620
-#define wxAuiPaneInfo_IsBottomDockable 2621
-#define wxAuiPaneInfo_IsDocked 2622
-#define wxAuiPaneInfo_IsFixed 2623
-#define wxAuiPaneInfo_IsFloatable 2624
-#define wxAuiPaneInfo_IsFloating 2625
-#define wxAuiPaneInfo_IsLeftDockable 2626
-#define wxAuiPaneInfo_IsMovable 2627
-#define wxAuiPaneInfo_IsOk 2628
-#define wxAuiPaneInfo_IsResizable 2629
-#define wxAuiPaneInfo_IsRightDockable 2630
-#define wxAuiPaneInfo_IsShown 2631
-#define wxAuiPaneInfo_IsToolbar 2632
-#define wxAuiPaneInfo_IsTopDockable 2633
-#define wxAuiPaneInfo_Layer 2634
-#define wxAuiPaneInfo_Left 2635
-#define wxAuiPaneInfo_LeftDockable 2636
-#define wxAuiPaneInfo_MaxSize_1 2637
-#define wxAuiPaneInfo_MaxSize_2 2638
-#define wxAuiPaneInfo_MaximizeButton 2639
-#define wxAuiPaneInfo_MinSize_1 2640
-#define wxAuiPaneInfo_MinSize_2 2641
-#define wxAuiPaneInfo_MinimizeButton 2642
-#define wxAuiPaneInfo_Movable 2643
-#define wxAuiPaneInfo_Name 2644
-#define wxAuiPaneInfo_PaneBorder 2645
-#define wxAuiPaneInfo_PinButton 2646
-#define wxAuiPaneInfo_Position 2647
-#define wxAuiPaneInfo_Resizable 2648
-#define wxAuiPaneInfo_Right 2649
-#define wxAuiPaneInfo_RightDockable 2650
-#define wxAuiPaneInfo_Row 2651
-#define wxAuiPaneInfo_SafeSet 2652
-#define wxAuiPaneInfo_SetFlag 2653
-#define wxAuiPaneInfo_Show 2654
-#define wxAuiPaneInfo_ToolbarPane 2655
-#define wxAuiPaneInfo_Top 2656
-#define wxAuiPaneInfo_TopDockable 2657
-#define wxAuiPaneInfo_Window 2658
-#define wxAuiPaneInfo_GetWindow 2659
-#define wxAuiPaneInfo_GetFrame 2660
-#define wxAuiPaneInfo_GetDirection 2661
-#define wxAuiPaneInfo_GetLayer 2662
-#define wxAuiPaneInfo_GetRow 2663
-#define wxAuiPaneInfo_GetPosition 2664
-#define wxAuiPaneInfo_GetFloatingPosition 2665
-#define wxAuiPaneInfo_GetFloatingSize 2666
-#define wxAuiNotebook_new_0 2667
-#define wxAuiNotebook_new_2 2668
-#define wxAuiNotebook_AddPage 2669
-#define wxAuiNotebook_Create 2670
-#define wxAuiNotebook_DeletePage 2671
-#define wxAuiNotebook_GetArtProvider 2672
-#define wxAuiNotebook_GetPage 2673
-#define wxAuiNotebook_GetPageBitmap 2674
-#define wxAuiNotebook_GetPageCount 2675
-#define wxAuiNotebook_GetPageIndex 2676
-#define wxAuiNotebook_GetPageText 2677
-#define wxAuiNotebook_GetSelection 2678
-#define wxAuiNotebook_InsertPage 2679
-#define wxAuiNotebook_RemovePage 2680
-#define wxAuiNotebook_SetArtProvider 2681
-#define wxAuiNotebook_SetFont 2682
-#define wxAuiNotebook_SetPageBitmap 2683
-#define wxAuiNotebook_SetPageText 2684
-#define wxAuiNotebook_SetSelection 2685
-#define wxAuiNotebook_SetTabCtrlHeight 2686
-#define wxAuiNotebook_SetUniformBitmapSize 2687
-#define wxAuiNotebook_destroy 2688
-#define wxAuiTabArt_SetFlags 2689
-#define wxAuiTabArt_SetMeasuringFont 2690
-#define wxAuiTabArt_SetNormalFont 2691
-#define wxAuiTabArt_SetSelectedFont 2692
-#define wxAuiTabArt_SetColour 2693
-#define wxAuiTabArt_SetActiveColour 2694
-#define wxAuiDockArt_GetColour 2695
-#define wxAuiDockArt_GetFont 2696
-#define wxAuiDockArt_GetMetric 2697
-#define wxAuiDockArt_SetColour 2698
-#define wxAuiDockArt_SetFont 2699
-#define wxAuiDockArt_SetMetric 2700
-#define wxAuiSimpleTabArt_new 2701
-#define wxAuiSimpleTabArt_destroy 2702
-#define wxMDIParentFrame_new_0 2703
-#define wxMDIParentFrame_new_4 2704
-#define wxMDIParentFrame_destruct 2705
-#define wxMDIParentFrame_ActivateNext 2706
-#define wxMDIParentFrame_ActivatePrevious 2707
-#define wxMDIParentFrame_ArrangeIcons 2708
-#define wxMDIParentFrame_Cascade 2709
-#define wxMDIParentFrame_Create 2710
-#define wxMDIParentFrame_GetActiveChild 2711
-#define wxMDIParentFrame_GetClientWindow 2712
-#define wxMDIParentFrame_Tile 2713
-#define wxMDIChildFrame_new_0 2714
-#define wxMDIChildFrame_new_4 2715
-#define wxMDIChildFrame_destruct 2716
-#define wxMDIChildFrame_Activate 2717
-#define wxMDIChildFrame_Create 2718
-#define wxMDIChildFrame_Maximize 2719
-#define wxMDIChildFrame_Restore 2720
-#define wxMDIClientWindow_new_0 2721
-#define wxMDIClientWindow_new_2 2722
-#define wxMDIClientWindow_destruct 2723
-#define wxMDIClientWindow_CreateClient 2724
-#define wxLayoutAlgorithm_new 2725
-#define wxLayoutAlgorithm_LayoutFrame 2726
-#define wxLayoutAlgorithm_LayoutMDIFrame 2727
-#define wxLayoutAlgorithm_LayoutWindow 2728
-#define wxLayoutAlgorithm_destroy 2729
-#define wxEvent_GetId 2730
-#define wxEvent_GetSkipped 2731
-#define wxEvent_GetTimestamp 2732
-#define wxEvent_IsCommandEvent 2733
-#define wxEvent_ResumePropagation 2734
-#define wxEvent_ShouldPropagate 2735
-#define wxEvent_Skip 2736
-#define wxEvent_StopPropagation 2737
-#define wxCommandEvent_getClientData 2738
-#define wxCommandEvent_GetExtraLong 2739
-#define wxCommandEvent_GetInt 2740
-#define wxCommandEvent_GetSelection 2741
-#define wxCommandEvent_GetString 2742
-#define wxCommandEvent_IsChecked 2743
-#define wxCommandEvent_IsSelection 2744
-#define wxCommandEvent_SetInt 2745
-#define wxCommandEvent_SetString 2746
-#define wxScrollEvent_GetOrientation 2747
-#define wxScrollEvent_GetPosition 2748
-#define wxScrollWinEvent_GetOrientation 2749
-#define wxScrollWinEvent_GetPosition 2750
-#define wxMouseEvent_AltDown 2751
-#define wxMouseEvent_Button 2752
-#define wxMouseEvent_ButtonDClick 2753
-#define wxMouseEvent_ButtonDown 2754
-#define wxMouseEvent_ButtonUp 2755
-#define wxMouseEvent_CmdDown 2756
-#define wxMouseEvent_ControlDown 2757
-#define wxMouseEvent_Dragging 2758
-#define wxMouseEvent_Entering 2759
-#define wxMouseEvent_GetButton 2760
-#define wxMouseEvent_GetPosition 2763
-#define wxMouseEvent_GetLogicalPosition 2764
-#define wxMouseEvent_GetLinesPerAction 2765
-#define wxMouseEvent_GetWheelRotation 2766
-#define wxMouseEvent_GetWheelDelta 2767
-#define wxMouseEvent_GetX 2768
-#define wxMouseEvent_GetY 2769
-#define wxMouseEvent_IsButton 2770
-#define wxMouseEvent_IsPageScroll 2771
-#define wxMouseEvent_Leaving 2772
-#define wxMouseEvent_LeftDClick 2773
-#define wxMouseEvent_LeftDown 2774
-#define wxMouseEvent_LeftIsDown 2775
-#define wxMouseEvent_LeftUp 2776
-#define wxMouseEvent_MetaDown 2777
-#define wxMouseEvent_MiddleDClick 2778
-#define wxMouseEvent_MiddleDown 2779
-#define wxMouseEvent_MiddleIsDown 2780
-#define wxMouseEvent_MiddleUp 2781
-#define wxMouseEvent_Moving 2782
-#define wxMouseEvent_RightDClick 2783
-#define wxMouseEvent_RightDown 2784
-#define wxMouseEvent_RightIsDown 2785
-#define wxMouseEvent_RightUp 2786
-#define wxMouseEvent_ShiftDown 2787
-#define wxSetCursorEvent_GetCursor 2788
-#define wxSetCursorEvent_GetX 2789
-#define wxSetCursorEvent_GetY 2790
-#define wxSetCursorEvent_HasCursor 2791
-#define wxSetCursorEvent_SetCursor 2792
-#define wxKeyEvent_AltDown 2793
-#define wxKeyEvent_CmdDown 2794
-#define wxKeyEvent_ControlDown 2795
-#define wxKeyEvent_GetKeyCode 2796
-#define wxKeyEvent_GetModifiers 2797
-#define wxKeyEvent_GetPosition 2800
-#define wxKeyEvent_GetRawKeyCode 2801
-#define wxKeyEvent_GetRawKeyFlags 2802
-#define wxKeyEvent_GetUnicodeKey 2803
-#define wxKeyEvent_GetX 2804
-#define wxKeyEvent_GetY 2805
-#define wxKeyEvent_HasModifiers 2806
-#define wxKeyEvent_MetaDown 2807
-#define wxKeyEvent_ShiftDown 2808
-#define wxSizeEvent_GetSize 2809
-#define wxMoveEvent_GetPosition 2810
-#define wxEraseEvent_GetDC 2811
-#define wxFocusEvent_GetWindow 2812
-#define wxChildFocusEvent_GetWindow 2813
-#define wxMenuEvent_GetMenu 2814
-#define wxMenuEvent_GetMenuId 2815
-#define wxMenuEvent_IsPopup 2816
-#define wxCloseEvent_CanVeto 2817
-#define wxCloseEvent_GetLoggingOff 2818
-#define wxCloseEvent_SetCanVeto 2819
-#define wxCloseEvent_SetLoggingOff 2820
-#define wxCloseEvent_Veto 2821
-#define wxShowEvent_SetShow 2822
-#define wxShowEvent_GetShow 2823
-#define wxIconizeEvent_Iconized 2824
-#define wxJoystickEvent_ButtonDown 2825
-#define wxJoystickEvent_ButtonIsDown 2826
-#define wxJoystickEvent_ButtonUp 2827
-#define wxJoystickEvent_GetButtonChange 2828
-#define wxJoystickEvent_GetButtonState 2829
-#define wxJoystickEvent_GetJoystick 2830
-#define wxJoystickEvent_GetPosition 2831
-#define wxJoystickEvent_GetZPosition 2832
-#define wxJoystickEvent_IsButton 2833
-#define wxJoystickEvent_IsMove 2834
-#define wxJoystickEvent_IsZMove 2835
-#define wxUpdateUIEvent_CanUpdate 2836
-#define wxUpdateUIEvent_Check 2837
-#define wxUpdateUIEvent_Enable 2838
-#define wxUpdateUIEvent_Show 2839
-#define wxUpdateUIEvent_GetChecked 2840
-#define wxUpdateUIEvent_GetEnabled 2841
-#define wxUpdateUIEvent_GetShown 2842
-#define wxUpdateUIEvent_GetSetChecked 2843
-#define wxUpdateUIEvent_GetSetEnabled 2844
-#define wxUpdateUIEvent_GetSetShown 2845
-#define wxUpdateUIEvent_GetSetText 2846
-#define wxUpdateUIEvent_GetText 2847
-#define wxUpdateUIEvent_GetMode 2848
-#define wxUpdateUIEvent_GetUpdateInterval 2849
-#define wxUpdateUIEvent_ResetUpdateTime 2850
-#define wxUpdateUIEvent_SetMode 2851
-#define wxUpdateUIEvent_SetText 2852
-#define wxUpdateUIEvent_SetUpdateInterval 2853
-#define wxMouseCaptureChangedEvent_GetCapturedWindow 2854
-#define wxPaletteChangedEvent_SetChangedWindow 2855
-#define wxPaletteChangedEvent_GetChangedWindow 2856
-#define wxQueryNewPaletteEvent_SetPaletteRealized 2857
-#define wxQueryNewPaletteEvent_GetPaletteRealized 2858
-#define wxNavigationKeyEvent_GetDirection 2859
-#define wxNavigationKeyEvent_SetDirection 2860
-#define wxNavigationKeyEvent_IsWindowChange 2861
-#define wxNavigationKeyEvent_SetWindowChange 2862
-#define wxNavigationKeyEvent_IsFromTab 2863
-#define wxNavigationKeyEvent_SetFromTab 2864
-#define wxNavigationKeyEvent_GetCurrentFocus 2865
-#define wxNavigationKeyEvent_SetCurrentFocus 2866
-#define wxHelpEvent_GetOrigin 2867
-#define wxHelpEvent_GetPosition 2868
-#define wxHelpEvent_SetOrigin 2869
-#define wxHelpEvent_SetPosition 2870
-#define wxContextMenuEvent_GetPosition 2871
-#define wxContextMenuEvent_SetPosition 2872
-#define wxIdleEvent_CanSend 2873
-#define wxIdleEvent_GetMode 2874
-#define wxIdleEvent_RequestMore 2875
-#define wxIdleEvent_MoreRequested 2876
-#define wxIdleEvent_SetMode 2877
-#define wxGridEvent_AltDown 2878
-#define wxGridEvent_ControlDown 2879
-#define wxGridEvent_GetCol 2880
-#define wxGridEvent_GetPosition 2881
-#define wxGridEvent_GetRow 2882
-#define wxGridEvent_MetaDown 2883
-#define wxGridEvent_Selecting 2884
-#define wxGridEvent_ShiftDown 2885
-#define wxNotifyEvent_Allow 2886
-#define wxNotifyEvent_IsAllowed 2887
-#define wxNotifyEvent_Veto 2888
-#define wxSashEvent_GetEdge 2889
-#define wxSashEvent_GetDragRect 2890
-#define wxSashEvent_GetDragStatus 2891
-#define wxListEvent_GetCacheFrom 2892
-#define wxListEvent_GetCacheTo 2893
-#define wxListEvent_GetKeyCode 2894
-#define wxListEvent_GetIndex 2895
-#define wxListEvent_GetColumn 2896
-#define wxListEvent_GetPoint 2897
-#define wxListEvent_GetLabel 2898
-#define wxListEvent_GetText 2899
-#define wxListEvent_GetImage 2900
-#define wxListEvent_GetData 2901
-#define wxListEvent_GetMask 2902
-#define wxListEvent_GetItem 2903
-#define wxListEvent_IsEditCancelled 2904
-#define wxDateEvent_GetDate 2905
-#define wxCalendarEvent_GetWeekDay 2906
-#define wxFileDirPickerEvent_GetPath 2907
-#define wxColourPickerEvent_GetColour 2908
-#define wxFontPickerEvent_GetFont 2909
-#define wxStyledTextEvent_GetPosition 2910
-#define wxStyledTextEvent_GetKey 2911
-#define wxStyledTextEvent_GetModifiers 2912
-#define wxStyledTextEvent_GetModificationType 2913
-#define wxStyledTextEvent_GetText 2914
-#define wxStyledTextEvent_GetLength 2915
-#define wxStyledTextEvent_GetLinesAdded 2916
-#define wxStyledTextEvent_GetLine 2917
-#define wxStyledTextEvent_GetFoldLevelNow 2918
-#define wxStyledTextEvent_GetFoldLevelPrev 2919
-#define wxStyledTextEvent_GetMargin 2920
-#define wxStyledTextEvent_GetMessage 2921
-#define wxStyledTextEvent_GetWParam 2922
-#define wxStyledTextEvent_GetLParam 2923
-#define wxStyledTextEvent_GetListType 2924
-#define wxStyledTextEvent_GetX 2925
-#define wxStyledTextEvent_GetY 2926
-#define wxStyledTextEvent_GetDragText 2927
-#define wxStyledTextEvent_GetDragAllowMove 2928
-#define wxStyledTextEvent_GetDragResult 2929
-#define wxStyledTextEvent_GetShift 2930
-#define wxStyledTextEvent_GetControl 2931
-#define wxStyledTextEvent_GetAlt 2932
-#define utils_wxGetKeyState 2933
-#define utils_wxGetMousePosition 2934
-#define utils_wxGetMouseState 2935
-#define utils_wxSetDetectableAutoRepeat 2936
-#define utils_wxBell 2937
-#define utils_wxFindMenuItemId 2938
-#define utils_wxGenericFindWindowAtPoint 2939
-#define utils_wxFindWindowAtPoint 2940
-#define utils_wxBeginBusyCursor 2941
-#define utils_wxEndBusyCursor 2942
-#define utils_wxIsBusy 2943
-#define utils_wxShutdown 2944
-#define utils_wxShell 2945
-#define utils_wxLaunchDefaultBrowser 2946
-#define utils_wxGetEmailAddress 2947
-#define utils_wxGetUserId 2948
-#define utils_wxGetHomeDir 2949
-#define utils_wxNewId 2950
-#define utils_wxRegisterId 2951
-#define utils_wxGetCurrentId 2952
-#define utils_wxGetOsDescription 2953
-#define utils_wxIsPlatformLittleEndian 2954
-#define utils_wxIsPlatform64Bit 2955
-#define gdicmn_wxDisplaySize 2956
-#define gdicmn_wxSetCursor 2957
-#define wxPrintout_new 2958
-#define wxPrintout_destruct 2959
-#define wxPrintout_GetDC 2960
-#define wxPrintout_GetPageSizeMM 2961
-#define wxPrintout_GetPageSizePixels 2962
-#define wxPrintout_GetPaperRectPixels 2963
-#define wxPrintout_GetPPIPrinter 2964
-#define wxPrintout_GetPPIScreen 2965
-#define wxPrintout_GetTitle 2966
-#define wxPrintout_IsPreview 2967
-#define wxPrintout_FitThisSizeToPaper 2968
-#define wxPrintout_FitThisSizeToPage 2969
-#define wxPrintout_FitThisSizeToPageMargins 2970
-#define wxPrintout_MapScreenSizeToPaper 2971
-#define wxPrintout_MapScreenSizeToPage 2972
-#define wxPrintout_MapScreenSizeToPageMargins 2973
-#define wxPrintout_MapScreenSizeToDevice 2974
-#define wxPrintout_GetLogicalPaperRect 2975
-#define wxPrintout_GetLogicalPageRect 2976
-#define wxPrintout_GetLogicalPageMarginsRect 2977
-#define wxPrintout_SetLogicalOrigin 2978
-#define wxPrintout_OffsetLogicalOrigin 2979
-#define wxStyledTextCtrl_new_2 2980
-#define wxStyledTextCtrl_new_0 2981
-#define wxStyledTextCtrl_destruct 2982
-#define wxStyledTextCtrl_Create 2983
-#define wxStyledTextCtrl_AddText 2984
-#define wxStyledTextCtrl_AddStyledText 2985
-#define wxStyledTextCtrl_InsertText 2986
-#define wxStyledTextCtrl_ClearAll 2987
-#define wxStyledTextCtrl_ClearDocumentStyle 2988
-#define wxStyledTextCtrl_GetLength 2989
-#define wxStyledTextCtrl_GetCharAt 2990
-#define wxStyledTextCtrl_GetCurrentPos 2991
-#define wxStyledTextCtrl_GetAnchor 2992
-#define wxStyledTextCtrl_GetStyleAt 2993
-#define wxStyledTextCtrl_Redo 2994
-#define wxStyledTextCtrl_SetUndoCollection 2995
-#define wxStyledTextCtrl_SelectAll 2996
-#define wxStyledTextCtrl_SetSavePoint 2997
-#define wxStyledTextCtrl_GetStyledText 2998
-#define wxStyledTextCtrl_CanRedo 2999
-#define wxStyledTextCtrl_MarkerLineFromHandle 3000
-#define wxStyledTextCtrl_MarkerDeleteHandle 3001
-#define wxStyledTextCtrl_GetUndoCollection 3002
-#define wxStyledTextCtrl_GetViewWhiteSpace 3003
-#define wxStyledTextCtrl_SetViewWhiteSpace 3004
-#define wxStyledTextCtrl_PositionFromPoint 3005
-#define wxStyledTextCtrl_PositionFromPointClose 3006
-#define wxStyledTextCtrl_GotoLine 3007
-#define wxStyledTextCtrl_GotoPos 3008
-#define wxStyledTextCtrl_SetAnchor 3009
-#define wxStyledTextCtrl_GetCurLine 3010
-#define wxStyledTextCtrl_GetEndStyled 3011
-#define wxStyledTextCtrl_ConvertEOLs 3012
-#define wxStyledTextCtrl_GetEOLMode 3013
-#define wxStyledTextCtrl_SetEOLMode 3014
-#define wxStyledTextCtrl_StartStyling 3015
-#define wxStyledTextCtrl_SetStyling 3016
-#define wxStyledTextCtrl_GetBufferedDraw 3017
-#define wxStyledTextCtrl_SetBufferedDraw 3018
-#define wxStyledTextCtrl_SetTabWidth 3019
-#define wxStyledTextCtrl_GetTabWidth 3020
-#define wxStyledTextCtrl_SetCodePage 3021
-#define wxStyledTextCtrl_MarkerDefine 3022
-#define wxStyledTextCtrl_MarkerSetForeground 3023
-#define wxStyledTextCtrl_MarkerSetBackground 3024
-#define wxStyledTextCtrl_MarkerAdd 3025
-#define wxStyledTextCtrl_MarkerDelete 3026
-#define wxStyledTextCtrl_MarkerDeleteAll 3027
-#define wxStyledTextCtrl_MarkerGet 3028
-#define wxStyledTextCtrl_MarkerNext 3029
-#define wxStyledTextCtrl_MarkerPrevious 3030
-#define wxStyledTextCtrl_MarkerDefineBitmap 3031
-#define wxStyledTextCtrl_MarkerAddSet 3032
-#define wxStyledTextCtrl_MarkerSetAlpha 3033
-#define wxStyledTextCtrl_SetMarginType 3034
-#define wxStyledTextCtrl_GetMarginType 3035
-#define wxStyledTextCtrl_SetMarginWidth 3036
-#define wxStyledTextCtrl_GetMarginWidth 3037
-#define wxStyledTextCtrl_SetMarginMask 3038
-#define wxStyledTextCtrl_GetMarginMask 3039
-#define wxStyledTextCtrl_SetMarginSensitive 3040
-#define wxStyledTextCtrl_GetMarginSensitive 3041
-#define wxStyledTextCtrl_StyleClearAll 3042
-#define wxStyledTextCtrl_StyleSetForeground 3043
-#define wxStyledTextCtrl_StyleSetBackground 3044
-#define wxStyledTextCtrl_StyleSetBold 3045
-#define wxStyledTextCtrl_StyleSetItalic 3046
-#define wxStyledTextCtrl_StyleSetSize 3047
-#define wxStyledTextCtrl_StyleSetFaceName 3048
-#define wxStyledTextCtrl_StyleSetEOLFilled 3049
-#define wxStyledTextCtrl_StyleResetDefault 3050
-#define wxStyledTextCtrl_StyleSetUnderline 3051
-#define wxStyledTextCtrl_StyleSetCase 3052
-#define wxStyledTextCtrl_StyleSetHotSpot 3053
-#define wxStyledTextCtrl_SetSelForeground 3054
-#define wxStyledTextCtrl_SetSelBackground 3055
-#define wxStyledTextCtrl_GetSelAlpha 3056
-#define wxStyledTextCtrl_SetSelAlpha 3057
-#define wxStyledTextCtrl_SetCaretForeground 3058
-#define wxStyledTextCtrl_CmdKeyAssign 3059
-#define wxStyledTextCtrl_CmdKeyClear 3060
-#define wxStyledTextCtrl_CmdKeyClearAll 3061
-#define wxStyledTextCtrl_SetStyleBytes 3062
-#define wxStyledTextCtrl_StyleSetVisible 3063
-#define wxStyledTextCtrl_GetCaretPeriod 3064
-#define wxStyledTextCtrl_SetCaretPeriod 3065
-#define wxStyledTextCtrl_SetWordChars 3066
-#define wxStyledTextCtrl_BeginUndoAction 3067
-#define wxStyledTextCtrl_EndUndoAction 3068
-#define wxStyledTextCtrl_IndicatorSetStyle 3069
-#define wxStyledTextCtrl_IndicatorGetStyle 3070
-#define wxStyledTextCtrl_IndicatorSetForeground 3071
-#define wxStyledTextCtrl_IndicatorGetForeground 3072
-#define wxStyledTextCtrl_SetWhitespaceForeground 3073
-#define wxStyledTextCtrl_SetWhitespaceBackground 3074
-#define wxStyledTextCtrl_GetStyleBits 3075
-#define wxStyledTextCtrl_SetLineState 3076
-#define wxStyledTextCtrl_GetLineState 3077
-#define wxStyledTextCtrl_GetMaxLineState 3078
-#define wxStyledTextCtrl_GetCaretLineVisible 3079
-#define wxStyledTextCtrl_SetCaretLineVisible 3080
-#define wxStyledTextCtrl_GetCaretLineBackground 3081
-#define wxStyledTextCtrl_SetCaretLineBackground 3082
-#define wxStyledTextCtrl_AutoCompShow 3083
-#define wxStyledTextCtrl_AutoCompCancel 3084
-#define wxStyledTextCtrl_AutoCompActive 3085
-#define wxStyledTextCtrl_AutoCompPosStart 3086
-#define wxStyledTextCtrl_AutoCompComplete 3087
-#define wxStyledTextCtrl_AutoCompStops 3088
-#define wxStyledTextCtrl_AutoCompSetSeparator 3089
-#define wxStyledTextCtrl_AutoCompGetSeparator 3090
-#define wxStyledTextCtrl_AutoCompSelect 3091
-#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3092
-#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3093
-#define wxStyledTextCtrl_AutoCompSetFillUps 3094
-#define wxStyledTextCtrl_AutoCompSetChooseSingle 3095
-#define wxStyledTextCtrl_AutoCompGetChooseSingle 3096
-#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3097
-#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3098
-#define wxStyledTextCtrl_UserListShow 3099
-#define wxStyledTextCtrl_AutoCompSetAutoHide 3100
-#define wxStyledTextCtrl_AutoCompGetAutoHide 3101
-#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3102
-#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3103
-#define wxStyledTextCtrl_RegisterImage 3104
-#define wxStyledTextCtrl_ClearRegisteredImages 3105
-#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3106
-#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3107
-#define wxStyledTextCtrl_AutoCompSetMaxWidth 3108
-#define wxStyledTextCtrl_AutoCompGetMaxWidth 3109
-#define wxStyledTextCtrl_AutoCompSetMaxHeight 3110
-#define wxStyledTextCtrl_AutoCompGetMaxHeight 3111
-#define wxStyledTextCtrl_SetIndent 3112
-#define wxStyledTextCtrl_GetIndent 3113
-#define wxStyledTextCtrl_SetUseTabs 3114
-#define wxStyledTextCtrl_GetUseTabs 3115
-#define wxStyledTextCtrl_SetLineIndentation 3116
-#define wxStyledTextCtrl_GetLineIndentation 3117
-#define wxStyledTextCtrl_GetLineIndentPosition 3118
-#define wxStyledTextCtrl_GetColumn 3119
-#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3120
-#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3121
-#define wxStyledTextCtrl_SetIndentationGuides 3122
-#define wxStyledTextCtrl_GetIndentationGuides 3123
-#define wxStyledTextCtrl_SetHighlightGuide 3124
-#define wxStyledTextCtrl_GetHighlightGuide 3125
-#define wxStyledTextCtrl_GetLineEndPosition 3126
-#define wxStyledTextCtrl_GetCodePage 3127
-#define wxStyledTextCtrl_GetCaretForeground 3128
-#define wxStyledTextCtrl_GetReadOnly 3129
-#define wxStyledTextCtrl_SetCurrentPos 3130
-#define wxStyledTextCtrl_SetSelectionStart 3131
-#define wxStyledTextCtrl_GetSelectionStart 3132
-#define wxStyledTextCtrl_SetSelectionEnd 3133
-#define wxStyledTextCtrl_GetSelectionEnd 3134
-#define wxStyledTextCtrl_SetPrintMagnification 3135
-#define wxStyledTextCtrl_GetPrintMagnification 3136
-#define wxStyledTextCtrl_SetPrintColourMode 3137
-#define wxStyledTextCtrl_GetPrintColourMode 3138
-#define wxStyledTextCtrl_FindText 3139
-#define wxStyledTextCtrl_FormatRange 3140
-#define wxStyledTextCtrl_GetFirstVisibleLine 3141
-#define wxStyledTextCtrl_GetLine 3142
-#define wxStyledTextCtrl_GetLineCount 3143
-#define wxStyledTextCtrl_SetMarginLeft 3144
-#define wxStyledTextCtrl_GetMarginLeft 3145
-#define wxStyledTextCtrl_SetMarginRight 3146
-#define wxStyledTextCtrl_GetMarginRight 3147
-#define wxStyledTextCtrl_GetModify 3148
-#define wxStyledTextCtrl_SetSelection 3149
-#define wxStyledTextCtrl_GetSelectedText 3150
-#define wxStyledTextCtrl_GetTextRange 3151
-#define wxStyledTextCtrl_HideSelection 3152
-#define wxStyledTextCtrl_LineFromPosition 3153
-#define wxStyledTextCtrl_PositionFromLine 3154
-#define wxStyledTextCtrl_LineScroll 3155
-#define wxStyledTextCtrl_EnsureCaretVisible 3156
-#define wxStyledTextCtrl_ReplaceSelection 3157
-#define wxStyledTextCtrl_SetReadOnly 3158
-#define wxStyledTextCtrl_CanPaste 3159
-#define wxStyledTextCtrl_CanUndo 3160
-#define wxStyledTextCtrl_EmptyUndoBuffer 3161
-#define wxStyledTextCtrl_Undo 3162
-#define wxStyledTextCtrl_Cut 3163
-#define wxStyledTextCtrl_Copy 3164
-#define wxStyledTextCtrl_Paste 3165
-#define wxStyledTextCtrl_Clear 3166
-#define wxStyledTextCtrl_SetText 3167
-#define wxStyledTextCtrl_GetText 3168
-#define wxStyledTextCtrl_GetTextLength 3169
-#define wxStyledTextCtrl_GetOvertype 3170
-#define wxStyledTextCtrl_SetCaretWidth 3171
-#define wxStyledTextCtrl_GetCaretWidth 3172
-#define wxStyledTextCtrl_SetTargetStart 3173
-#define wxStyledTextCtrl_GetTargetStart 3174
-#define wxStyledTextCtrl_SetTargetEnd 3175
-#define wxStyledTextCtrl_GetTargetEnd 3176
-#define wxStyledTextCtrl_ReplaceTarget 3177
-#define wxStyledTextCtrl_SearchInTarget 3178
-#define wxStyledTextCtrl_SetSearchFlags 3179
-#define wxStyledTextCtrl_GetSearchFlags 3180
-#define wxStyledTextCtrl_CallTipShow 3181
-#define wxStyledTextCtrl_CallTipCancel 3182
-#define wxStyledTextCtrl_CallTipActive 3183
-#define wxStyledTextCtrl_CallTipPosAtStart 3184
-#define wxStyledTextCtrl_CallTipSetHighlight 3185
-#define wxStyledTextCtrl_CallTipSetBackground 3186
-#define wxStyledTextCtrl_CallTipSetForeground 3187
-#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3188
-#define wxStyledTextCtrl_CallTipUseStyle 3189
-#define wxStyledTextCtrl_VisibleFromDocLine 3190
-#define wxStyledTextCtrl_DocLineFromVisible 3191
-#define wxStyledTextCtrl_WrapCount 3192
-#define wxStyledTextCtrl_SetFoldLevel 3193
-#define wxStyledTextCtrl_GetFoldLevel 3194
-#define wxStyledTextCtrl_GetLastChild 3195
-#define wxStyledTextCtrl_GetFoldParent 3196
-#define wxStyledTextCtrl_ShowLines 3197
-#define wxStyledTextCtrl_HideLines 3198
-#define wxStyledTextCtrl_GetLineVisible 3199
-#define wxStyledTextCtrl_SetFoldExpanded 3200
-#define wxStyledTextCtrl_GetFoldExpanded 3201
-#define wxStyledTextCtrl_ToggleFold 3202
-#define wxStyledTextCtrl_EnsureVisible 3203
-#define wxStyledTextCtrl_SetFoldFlags 3204
-#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3205
-#define wxStyledTextCtrl_SetTabIndents 3206
-#define wxStyledTextCtrl_GetTabIndents 3207
-#define wxStyledTextCtrl_SetBackSpaceUnIndents 3208
-#define wxStyledTextCtrl_GetBackSpaceUnIndents 3209
-#define wxStyledTextCtrl_SetMouseDwellTime 3210
-#define wxStyledTextCtrl_GetMouseDwellTime 3211
-#define wxStyledTextCtrl_WordStartPosition 3212
-#define wxStyledTextCtrl_WordEndPosition 3213
-#define wxStyledTextCtrl_SetWrapMode 3214
-#define wxStyledTextCtrl_GetWrapMode 3215
-#define wxStyledTextCtrl_SetWrapVisualFlags 3216
-#define wxStyledTextCtrl_GetWrapVisualFlags 3217
-#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3218
-#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3219
-#define wxStyledTextCtrl_SetWrapStartIndent 3220
-#define wxStyledTextCtrl_GetWrapStartIndent 3221
-#define wxStyledTextCtrl_SetLayoutCache 3222
-#define wxStyledTextCtrl_GetLayoutCache 3223
-#define wxStyledTextCtrl_SetScrollWidth 3224
-#define wxStyledTextCtrl_GetScrollWidth 3225
-#define wxStyledTextCtrl_TextWidth 3226
-#define wxStyledTextCtrl_GetEndAtLastLine 3227
-#define wxStyledTextCtrl_TextHeight 3228
-#define wxStyledTextCtrl_SetUseVerticalScrollBar 3229
-#define wxStyledTextCtrl_GetUseVerticalScrollBar 3230
-#define wxStyledTextCtrl_AppendText 3231
-#define wxStyledTextCtrl_GetTwoPhaseDraw 3232
-#define wxStyledTextCtrl_SetTwoPhaseDraw 3233
-#define wxStyledTextCtrl_TargetFromSelection 3234
-#define wxStyledTextCtrl_LinesJoin 3235
-#define wxStyledTextCtrl_LinesSplit 3236
-#define wxStyledTextCtrl_SetFoldMarginColour 3237
-#define wxStyledTextCtrl_SetFoldMarginHiColour 3238
-#define wxStyledTextCtrl_LineDown 3239
-#define wxStyledTextCtrl_LineDownExtend 3240
-#define wxStyledTextCtrl_LineUp 3241
-#define wxStyledTextCtrl_LineUpExtend 3242
-#define wxStyledTextCtrl_CharLeft 3243
-#define wxStyledTextCtrl_CharLeftExtend 3244
-#define wxStyledTextCtrl_CharRight 3245
-#define wxStyledTextCtrl_CharRightExtend 3246
-#define wxStyledTextCtrl_WordLeft 3247
-#define wxStyledTextCtrl_WordLeftExtend 3248
-#define wxStyledTextCtrl_WordRight 3249
-#define wxStyledTextCtrl_WordRightExtend 3250
-#define wxStyledTextCtrl_Home 3251
-#define wxStyledTextCtrl_HomeExtend 3252
-#define wxStyledTextCtrl_LineEnd 3253
-#define wxStyledTextCtrl_LineEndExtend 3254
-#define wxStyledTextCtrl_DocumentStart 3255
-#define wxStyledTextCtrl_DocumentStartExtend 3256
-#define wxStyledTextCtrl_DocumentEnd 3257
-#define wxStyledTextCtrl_DocumentEndExtend 3258
-#define wxStyledTextCtrl_PageUp 3259
-#define wxStyledTextCtrl_PageUpExtend 3260
-#define wxStyledTextCtrl_PageDown 3261
-#define wxStyledTextCtrl_PageDownExtend 3262
-#define wxStyledTextCtrl_EditToggleOvertype 3263
-#define wxStyledTextCtrl_Cancel 3264
-#define wxStyledTextCtrl_DeleteBack 3265
-#define wxStyledTextCtrl_Tab 3266
-#define wxStyledTextCtrl_BackTab 3267
-#define wxStyledTextCtrl_NewLine 3268
-#define wxStyledTextCtrl_FormFeed 3269
-#define wxStyledTextCtrl_VCHome 3270
-#define wxStyledTextCtrl_VCHomeExtend 3271
-#define wxStyledTextCtrl_ZoomIn 3272
-#define wxStyledTextCtrl_ZoomOut 3273
-#define wxStyledTextCtrl_DelWordLeft 3274
-#define wxStyledTextCtrl_DelWordRight 3275
-#define wxStyledTextCtrl_LineCut 3276
-#define wxStyledTextCtrl_LineDelete 3277
-#define wxStyledTextCtrl_LineTranspose 3278
-#define wxStyledTextCtrl_LineDuplicate 3279
-#define wxStyledTextCtrl_LowerCase 3280
-#define wxStyledTextCtrl_UpperCase 3281
-#define wxStyledTextCtrl_LineScrollDown 3282
-#define wxStyledTextCtrl_LineScrollUp 3283
-#define wxStyledTextCtrl_DeleteBackNotLine 3284
-#define wxStyledTextCtrl_HomeDisplay 3285
-#define wxStyledTextCtrl_HomeDisplayExtend 3286
-#define wxStyledTextCtrl_LineEndDisplay 3287
-#define wxStyledTextCtrl_LineEndDisplayExtend 3288
-#define wxStyledTextCtrl_HomeWrapExtend 3289
-#define wxStyledTextCtrl_LineEndWrap 3290
-#define wxStyledTextCtrl_LineEndWrapExtend 3291
-#define wxStyledTextCtrl_VCHomeWrap 3292
-#define wxStyledTextCtrl_VCHomeWrapExtend 3293
-#define wxStyledTextCtrl_LineCopy 3294
-#define wxStyledTextCtrl_MoveCaretInsideView 3295
-#define wxStyledTextCtrl_LineLength 3296
-#define wxStyledTextCtrl_BraceHighlight 3297
-#define wxStyledTextCtrl_BraceBadLight 3298
-#define wxStyledTextCtrl_BraceMatch 3299
-#define wxStyledTextCtrl_GetViewEOL 3300
-#define wxStyledTextCtrl_SetViewEOL 3301
-#define wxStyledTextCtrl_SetModEventMask 3302
-#define wxStyledTextCtrl_GetEdgeColumn 3303
-#define wxStyledTextCtrl_SetEdgeColumn 3304
-#define wxStyledTextCtrl_SetEdgeMode 3305
-#define wxStyledTextCtrl_GetEdgeMode 3306
-#define wxStyledTextCtrl_GetEdgeColour 3307
-#define wxStyledTextCtrl_SetEdgeColour 3308
-#define wxStyledTextCtrl_SearchAnchor 3309
-#define wxStyledTextCtrl_SearchNext 3310
-#define wxStyledTextCtrl_SearchPrev 3311
-#define wxStyledTextCtrl_LinesOnScreen 3312
-#define wxStyledTextCtrl_UsePopUp 3313
-#define wxStyledTextCtrl_SelectionIsRectangle 3314
-#define wxStyledTextCtrl_SetZoom 3315
-#define wxStyledTextCtrl_GetZoom 3316
-#define wxStyledTextCtrl_GetModEventMask 3317
-#define wxStyledTextCtrl_SetSTCFocus 3318
-#define wxStyledTextCtrl_GetSTCFocus 3319
-#define wxStyledTextCtrl_SetStatus 3320
-#define wxStyledTextCtrl_GetStatus 3321
-#define wxStyledTextCtrl_SetMouseDownCaptures 3322
-#define wxStyledTextCtrl_GetMouseDownCaptures 3323
-#define wxStyledTextCtrl_SetSTCCursor 3324
-#define wxStyledTextCtrl_GetSTCCursor 3325
-#define wxStyledTextCtrl_SetControlCharSymbol 3326
-#define wxStyledTextCtrl_GetControlCharSymbol 3327
-#define wxStyledTextCtrl_WordPartLeft 3328
-#define wxStyledTextCtrl_WordPartLeftExtend 3329
-#define wxStyledTextCtrl_WordPartRight 3330
-#define wxStyledTextCtrl_WordPartRightExtend 3331
-#define wxStyledTextCtrl_SetVisiblePolicy 3332
-#define wxStyledTextCtrl_DelLineLeft 3333
-#define wxStyledTextCtrl_DelLineRight 3334
-#define wxStyledTextCtrl_GetXOffset 3335
-#define wxStyledTextCtrl_ChooseCaretX 3336
-#define wxStyledTextCtrl_SetXCaretPolicy 3337
-#define wxStyledTextCtrl_SetYCaretPolicy 3338
-#define wxStyledTextCtrl_GetPrintWrapMode 3339
-#define wxStyledTextCtrl_SetHotspotActiveForeground 3340
-#define wxStyledTextCtrl_SetHotspotActiveBackground 3341
-#define wxStyledTextCtrl_SetHotspotActiveUnderline 3342
-#define wxStyledTextCtrl_SetHotspotSingleLine 3343
-#define wxStyledTextCtrl_ParaDownExtend 3344
-#define wxStyledTextCtrl_ParaUp 3345
-#define wxStyledTextCtrl_ParaUpExtend 3346
-#define wxStyledTextCtrl_PositionBefore 3347
-#define wxStyledTextCtrl_PositionAfter 3348
-#define wxStyledTextCtrl_CopyRange 3349
-#define wxStyledTextCtrl_CopyText 3350
-#define wxStyledTextCtrl_SetSelectionMode 3351
-#define wxStyledTextCtrl_GetSelectionMode 3352
-#define wxStyledTextCtrl_LineDownRectExtend 3353
-#define wxStyledTextCtrl_LineUpRectExtend 3354
-#define wxStyledTextCtrl_CharLeftRectExtend 3355
-#define wxStyledTextCtrl_CharRightRectExtend 3356
-#define wxStyledTextCtrl_HomeRectExtend 3357
-#define wxStyledTextCtrl_VCHomeRectExtend 3358
-#define wxStyledTextCtrl_LineEndRectExtend 3359
-#define wxStyledTextCtrl_PageUpRectExtend 3360
-#define wxStyledTextCtrl_PageDownRectExtend 3361
-#define wxStyledTextCtrl_StutteredPageUp 3362
-#define wxStyledTextCtrl_StutteredPageUpExtend 3363
-#define wxStyledTextCtrl_StutteredPageDown 3364
-#define wxStyledTextCtrl_StutteredPageDownExtend 3365
-#define wxStyledTextCtrl_WordLeftEnd 3366
-#define wxStyledTextCtrl_WordLeftEndExtend 3367
-#define wxStyledTextCtrl_WordRightEnd 3368
-#define wxStyledTextCtrl_WordRightEndExtend 3369
-#define wxStyledTextCtrl_SetWhitespaceChars 3370
-#define wxStyledTextCtrl_SetCharsDefault 3371
-#define wxStyledTextCtrl_AutoCompGetCurrent 3372
-#define wxStyledTextCtrl_Allocate 3373
-#define wxStyledTextCtrl_FindColumn 3374
-#define wxStyledTextCtrl_GetCaretSticky 3375
-#define wxStyledTextCtrl_SetCaretSticky 3376
-#define wxStyledTextCtrl_ToggleCaretSticky 3377
-#define wxStyledTextCtrl_SetPasteConvertEndings 3378
-#define wxStyledTextCtrl_GetPasteConvertEndings 3379
-#define wxStyledTextCtrl_SelectionDuplicate 3380
-#define wxStyledTextCtrl_SetCaretLineBackAlpha 3381
-#define wxStyledTextCtrl_GetCaretLineBackAlpha 3382
-#define wxStyledTextCtrl_StartRecord 3383
-#define wxStyledTextCtrl_StopRecord 3384
-#define wxStyledTextCtrl_SetLexer 3385
-#define wxStyledTextCtrl_GetLexer 3386
-#define wxStyledTextCtrl_Colourise 3387
-#define wxStyledTextCtrl_SetProperty 3388
-#define wxStyledTextCtrl_SetKeyWords 3389
-#define wxStyledTextCtrl_SetLexerLanguage 3390
-#define wxStyledTextCtrl_GetProperty 3391
-#define wxStyledTextCtrl_GetStyleBitsNeeded 3392
-#define wxStyledTextCtrl_GetCurrentLine 3393
-#define wxStyledTextCtrl_StyleSetSpec 3394
-#define wxStyledTextCtrl_StyleSetFont 3395
-#define wxStyledTextCtrl_StyleSetFontAttr 3396
-#define wxStyledTextCtrl_StyleSetCharacterSet 3397
-#define wxStyledTextCtrl_StyleSetFontEncoding 3398
-#define wxStyledTextCtrl_CmdKeyExecute 3399
-#define wxStyledTextCtrl_SetMargins 3400
-#define wxStyledTextCtrl_GetSelection 3401
-#define wxStyledTextCtrl_PointFromPosition 3402
-#define wxStyledTextCtrl_ScrollToLine 3403
-#define wxStyledTextCtrl_ScrollToColumn 3404
-#define wxStyledTextCtrl_SetVScrollBar 3405
-#define wxStyledTextCtrl_SetHScrollBar 3406
-#define wxStyledTextCtrl_GetLastKeydownProcessed 3407
-#define wxStyledTextCtrl_SetLastKeydownProcessed 3408
-#define wxStyledTextCtrl_SaveFile 3409
-#define wxStyledTextCtrl_LoadFile 3410
-#define wxStyledTextCtrl_DoDragOver 3411
-#define wxStyledTextCtrl_DoDropText 3412
-#define wxStyledTextCtrl_GetUseAntiAliasing 3413
-#define wxStyledTextCtrl_AddTextRaw 3414
-#define wxStyledTextCtrl_InsertTextRaw 3415
-#define wxStyledTextCtrl_GetCurLineRaw 3416
-#define wxStyledTextCtrl_GetLineRaw 3417
-#define wxStyledTextCtrl_GetSelectedTextRaw 3418
-#define wxStyledTextCtrl_GetTextRangeRaw 3419
-#define wxStyledTextCtrl_SetTextRaw 3420
-#define wxStyledTextCtrl_GetTextRaw 3421
-#define wxStyledTextCtrl_AppendTextRaw 3422
-#define wxArtProvider_GetBitmap 3423
-#define wxArtProvider_GetIcon 3424
-#define wxTreeEvent_GetKeyCode 3425
-#define wxTreeEvent_GetItem 3426
-#define wxTreeEvent_GetKeyEvent 3427
-#define wxTreeEvent_GetLabel 3428
-#define wxTreeEvent_GetOldItem 3429
-#define wxTreeEvent_GetPoint 3430
-#define wxTreeEvent_IsEditCancelled 3431
-#define wxTreeEvent_SetToolTip 3432
-#define wxNotebookEvent_GetOldSelection 3433
-#define wxNotebookEvent_GetSelection 3434
-#define wxNotebookEvent_SetOldSelection 3435
-#define wxNotebookEvent_SetSelection 3436
-#define wxFileDataObject_new 3437
-#define wxFileDataObject_AddFile 3438
-#define wxFileDataObject_GetFilenames 3439
-#define wxFileDataObject_destroy 3440
-#define wxTextDataObject_new 3441
-#define wxTextDataObject_GetTextLength 3442
-#define wxTextDataObject_GetText 3443
-#define wxTextDataObject_SetText 3444
-#define wxTextDataObject_destroy 3445
-#define wxBitmapDataObject_new_1_1 3446
-#define wxBitmapDataObject_new_1_0 3447
-#define wxBitmapDataObject_GetBitmap 3448
-#define wxBitmapDataObject_SetBitmap 3449
-#define wxBitmapDataObject_destroy 3450
-#define wxClipboard_new 3452
-#define wxClipboard_destruct 3453
-#define wxClipboard_AddData 3454
-#define wxClipboard_Clear 3455
-#define wxClipboard_Close 3456
-#define wxClipboard_Flush 3457
-#define wxClipboard_GetData 3458
-#define wxClipboard_IsOpened 3459
-#define wxClipboard_Open 3460
-#define wxClipboard_SetData 3461
-#define wxClipboard_UsePrimarySelection 3463
-#define wxClipboard_IsSupported 3464
-#define wxClipboard_Get 3465
-#define wxSpinEvent_GetPosition 3466
-#define wxSpinEvent_SetPosition 3467
-#define wxSplitterWindow_new_0 3468
-#define wxSplitterWindow_new_2 3469
-#define wxSplitterWindow_destruct 3470
-#define wxSplitterWindow_Create 3471
-#define wxSplitterWindow_GetMinimumPaneSize 3472
-#define wxSplitterWindow_GetSashGravity 3473
-#define wxSplitterWindow_GetSashPosition 3474
-#define wxSplitterWindow_GetSplitMode 3475
-#define wxSplitterWindow_GetWindow1 3476
-#define wxSplitterWindow_GetWindow2 3477
-#define wxSplitterWindow_Initialize 3478
-#define wxSplitterWindow_IsSplit 3479
-#define wxSplitterWindow_ReplaceWindow 3480
-#define wxSplitterWindow_SetSashGravity 3481
-#define wxSplitterWindow_SetSashPosition 3482
-#define wxSplitterWindow_SetSashSize 3483
-#define wxSplitterWindow_SetMinimumPaneSize 3484
-#define wxSplitterWindow_SetSplitMode 3485
-#define wxSplitterWindow_SplitHorizontally 3486
-#define wxSplitterWindow_SplitVertically 3487
-#define wxSplitterWindow_Unsplit 3488
-#define wxSplitterWindow_UpdateSize 3489
-#define wxSplitterEvent_GetSashPosition 3490
-#define wxSplitterEvent_GetX 3491
-#define wxSplitterEvent_GetY 3492
-#define wxSplitterEvent_GetWindowBeingRemoved 3493
-#define wxSplitterEvent_SetSashPosition 3494
-#define wxHtmlWindow_new_0 3495
-#define wxHtmlWindow_new_2 3496
-#define wxHtmlWindow_AppendToPage 3497
-#define wxHtmlWindow_GetOpenedAnchor 3498
-#define wxHtmlWindow_GetOpenedPage 3499
-#define wxHtmlWindow_GetOpenedPageTitle 3500
-#define wxHtmlWindow_GetRelatedFrame 3501
-#define wxHtmlWindow_HistoryBack 3502
-#define wxHtmlWindow_HistoryCanBack 3503
-#define wxHtmlWindow_HistoryCanForward 3504
-#define wxHtmlWindow_HistoryClear 3505
-#define wxHtmlWindow_HistoryForward 3506
-#define wxHtmlWindow_LoadFile 3507
-#define wxHtmlWindow_LoadPage 3508
-#define wxHtmlWindow_SelectAll 3509
-#define wxHtmlWindow_SelectionToText 3510
-#define wxHtmlWindow_SelectLine 3511
-#define wxHtmlWindow_SelectWord 3512
-#define wxHtmlWindow_SetBorders 3513
-#define wxHtmlWindow_SetFonts 3514
-#define wxHtmlWindow_SetPage 3515
-#define wxHtmlWindow_SetRelatedFrame 3516
-#define wxHtmlWindow_SetRelatedStatusBar 3517
-#define wxHtmlWindow_ToText 3518
-#define wxHtmlWindow_destroy 3519
-#define wxHtmlLinkEvent_GetLinkInfo 3520
-#define wxSystemSettings_GetColour 3521
-#define wxSystemSettings_GetFont 3522
-#define wxSystemSettings_GetMetric 3523
-#define wxSystemSettings_GetScreenType 3524
-#define wxSystemOptions_GetOption 3525
-#define wxSystemOptions_GetOptionInt 3526
-#define wxSystemOptions_HasOption 3527
-#define wxSystemOptions_IsFalse 3528
-#define wxSystemOptions_SetOption_2_1 3529
-#define wxSystemOptions_SetOption_2_0 3530
-#define wxAuiNotebookEvent_SetSelection 3531
-#define wxAuiNotebookEvent_GetSelection 3532
-#define wxAuiNotebookEvent_SetOldSelection 3533
-#define wxAuiNotebookEvent_GetOldSelection 3534
-#define wxAuiNotebookEvent_SetDragSource 3535
-#define wxAuiNotebookEvent_GetDragSource 3536
-#define wxAuiManagerEvent_SetManager 3537
-#define wxAuiManagerEvent_GetManager 3538
-#define wxAuiManagerEvent_SetPane 3539
-#define wxAuiManagerEvent_GetPane 3540
-#define wxAuiManagerEvent_SetButton 3541
-#define wxAuiManagerEvent_GetButton 3542
-#define wxAuiManagerEvent_SetDC 3543
-#define wxAuiManagerEvent_GetDC 3544
-#define wxAuiManagerEvent_Veto 3545
-#define wxAuiManagerEvent_GetVeto 3546
-#define wxAuiManagerEvent_SetCanVeto 3547
-#define wxAuiManagerEvent_CanVeto 3548
-#define wxLogNull_new 3549
-#define wxLogNull_destroy 3550
-#define wxTaskBarIcon_new 3551
-#define wxTaskBarIcon_destruct 3552
-#define wxTaskBarIcon_PopupMenu 3553
-#define wxTaskBarIcon_RemoveIcon 3554
-#define wxTaskBarIcon_SetIcon 3555
-#define wxLocale_new_0 3556
-#define wxLocale_new_2 3558
-#define wxLocale_destruct 3559
-#define wxLocale_Init 3561
-#define wxLocale_AddCatalog_1 3562
-#define wxLocale_AddCatalog_3 3563
-#define wxLocale_AddCatalogLookupPathPrefix 3564
-#define wxLocale_GetCanonicalName 3565
-#define wxLocale_GetLanguage 3566
-#define wxLocale_GetLanguageName 3567
-#define wxLocale_GetLocale 3568
-#define wxLocale_GetName 3569
-#define wxLocale_GetString_2 3570
-#define wxLocale_GetString_4 3571
-#define wxLocale_GetHeaderValue 3572
-#define wxLocale_GetSysName 3573
-#define wxLocale_GetSystemEncoding 3574
-#define wxLocale_GetSystemEncodingName 3575
-#define wxLocale_GetSystemLanguage 3576
-#define wxLocale_IsLoaded 3577
-#define wxLocale_IsOk 3578
-#define wxActivateEvent_GetActive 3579
-#define wxPopupWindow_new_2 3581
-#define wxPopupWindow_new_0 3582
-#define wxPopupWindow_destruct 3584
-#define wxPopupWindow_Create 3585
-#define wxPopupWindow_Position 3586
-#define wxPopupTransientWindow_new_0 3587
-#define wxPopupTransientWindow_new_2 3588
-#define wxPopupTransientWindow_destruct 3589
-#define wxPopupTransientWindow_Popup 3590
-#define wxPopupTransientWindow_Dismiss 3591
-#define wxOverlay_new 3592
-#define wxOverlay_destruct 3593
-#define wxOverlay_Reset 3594
-#define wxDCOverlay_new_6 3595
-#define wxDCOverlay_new_2 3596
-#define wxDCOverlay_destruct 3597
-#define wxDCOverlay_Clear 3598
+#define wxGauge_GetRange 1605
+#define wxGauge_GetValue 1606
+#define wxGauge_IsVertical 1607
+#define wxGauge_SetRange 1608
+#define wxGauge_SetValue 1609
+#define wxGauge_Pulse 1610
+#define wxGauge_destroy 1611
+#define wxGenericDirCtrl_new_0 1612
+#define wxGenericDirCtrl_new_2 1613
+#define wxGenericDirCtrl_destruct 1614
+#define wxGenericDirCtrl_Create 1615
+#define wxGenericDirCtrl_Init 1616
+#define wxGenericDirCtrl_CollapseTree 1617
+#define wxGenericDirCtrl_ExpandPath 1618
+#define wxGenericDirCtrl_GetDefaultPath 1619
+#define wxGenericDirCtrl_GetPath 1620
+#define wxGenericDirCtrl_GetFilePath 1621
+#define wxGenericDirCtrl_GetFilter 1622
+#define wxGenericDirCtrl_GetFilterIndex 1623
+#define wxGenericDirCtrl_GetRootId 1624
+#define wxGenericDirCtrl_GetTreeCtrl 1625
+#define wxGenericDirCtrl_ReCreateTree 1626
+#define wxGenericDirCtrl_SetDefaultPath 1627
+#define wxGenericDirCtrl_SetFilter 1628
+#define wxGenericDirCtrl_SetFilterIndex 1629
+#define wxGenericDirCtrl_SetPath 1630
+#define wxStaticBox_new_4 1632
+#define wxStaticBox_new_0 1633
+#define wxStaticBox_Create 1634
+#define wxStaticBox_destroy 1635
+#define wxStaticLine_new_2 1637
+#define wxStaticLine_new_0 1638
+#define wxStaticLine_Create 1639
+#define wxStaticLine_IsVertical 1640
+#define wxStaticLine_GetDefaultSize 1641
+#define wxStaticLine_destroy 1642
+#define wxListBox_new_3 1645
+#define wxListBox_new_0 1646
+#define wxListBox_destruct 1648
+#define wxListBox_Create 1650
+#define wxListBox_Deselect 1651
+#define wxListBox_GetSelections 1652
+#define wxListBox_InsertItems 1653
+#define wxListBox_IsSelected 1654
+#define wxListBox_Set 1655
+#define wxListBox_HitTest 1656
+#define wxListBox_SetFirstItem_1_0 1657
+#define wxListBox_SetFirstItem_1_1 1658
+#define wxListCtrl_new_0 1659
+#define wxListCtrl_new_2 1660
+#define wxListCtrl_Arrange 1661
+#define wxListCtrl_AssignImageList 1662
+#define wxListCtrl_ClearAll 1663
+#define wxListCtrl_Create 1664
+#define wxListCtrl_DeleteAllItems 1665
+#define wxListCtrl_DeleteColumn 1666
+#define wxListCtrl_DeleteItem 1667
+#define wxListCtrl_EditLabel 1668
+#define wxListCtrl_EnsureVisible 1669
+#define wxListCtrl_FindItem_3_0 1670
+#define wxListCtrl_FindItem_3_1 1671
+#define wxListCtrl_GetColumn 1672
+#define wxListCtrl_GetColumnCount 1673
+#define wxListCtrl_GetColumnWidth 1674
+#define wxListCtrl_GetCountPerPage 1675
+#define wxListCtrl_GetEditControl 1676
+#define wxListCtrl_GetImageList 1677
+#define wxListCtrl_GetItem 1678
+#define wxListCtrl_GetItemBackgroundColour 1679
+#define wxListCtrl_GetItemCount 1680
+#define wxListCtrl_GetItemData 1681
+#define wxListCtrl_GetItemFont 1682
+#define wxListCtrl_GetItemPosition 1683
+#define wxListCtrl_GetItemRect 1684
+#define wxListCtrl_GetItemSpacing 1685
+#define wxListCtrl_GetItemState 1686
+#define wxListCtrl_GetItemText 1687
+#define wxListCtrl_GetItemTextColour 1688
+#define wxListCtrl_GetNextItem 1689
+#define wxListCtrl_GetSelectedItemCount 1690
+#define wxListCtrl_GetTextColour 1691
+#define wxListCtrl_GetTopItem 1692
+#define wxListCtrl_GetViewRect 1693
+#define wxListCtrl_HitTest 1694
+#define wxListCtrl_InsertColumn_2 1695
+#define wxListCtrl_InsertColumn_3 1696
+#define wxListCtrl_InsertItem_1 1697
+#define wxListCtrl_InsertItem_2_1 1698
+#define wxListCtrl_InsertItem_2_0 1699
+#define wxListCtrl_InsertItem_3 1700
+#define wxListCtrl_RefreshItem 1701
+#define wxListCtrl_RefreshItems 1702
+#define wxListCtrl_ScrollList 1703
+#define wxListCtrl_SetBackgroundColour 1704
+#define wxListCtrl_SetColumn 1705
+#define wxListCtrl_SetColumnWidth 1706
+#define wxListCtrl_SetImageList 1707
+#define wxListCtrl_SetItem_1 1708
+#define wxListCtrl_SetItem_4 1709
+#define wxListCtrl_SetItemBackgroundColour 1710
+#define wxListCtrl_SetItemCount 1711
+#define wxListCtrl_SetItemData 1712
+#define wxListCtrl_SetItemFont 1713
+#define wxListCtrl_SetItemImage 1714
+#define wxListCtrl_SetItemColumnImage 1715
+#define wxListCtrl_SetItemPosition 1716
+#define wxListCtrl_SetItemState 1717
+#define wxListCtrl_SetItemText 1718
+#define wxListCtrl_SetItemTextColour 1719
+#define wxListCtrl_SetSingleStyle 1720
+#define wxListCtrl_SetTextColour 1721
+#define wxListCtrl_SetWindowStyleFlag 1722
+#define wxListCtrl_SortItems 1723
+#define wxListCtrl_destroy 1724
+#define wxListView_ClearColumnImage 1725
+#define wxListView_Focus 1726
+#define wxListView_GetFirstSelected 1727
+#define wxListView_GetFocusedItem 1728
+#define wxListView_GetNextSelected 1729
+#define wxListView_IsSelected 1730
+#define wxListView_Select 1731
+#define wxListView_SetColumnImage 1732
+#define wxListItem_new_0 1733
+#define wxListItem_new_1 1734
+#define wxListItem_destruct 1735
+#define wxListItem_Clear 1736
+#define wxListItem_GetAlign 1737
+#define wxListItem_GetBackgroundColour 1738
+#define wxListItem_GetColumn 1739
+#define wxListItem_GetFont 1740
+#define wxListItem_GetId 1741
+#define wxListItem_GetImage 1742
+#define wxListItem_GetMask 1743
+#define wxListItem_GetState 1744
+#define wxListItem_GetText 1745
+#define wxListItem_GetTextColour 1746
+#define wxListItem_GetWidth 1747
+#define wxListItem_SetAlign 1748
+#define wxListItem_SetBackgroundColour 1749
+#define wxListItem_SetColumn 1750
+#define wxListItem_SetFont 1751
+#define wxListItem_SetId 1752
+#define wxListItem_SetImage 1753
+#define wxListItem_SetMask 1754
+#define wxListItem_SetState 1755
+#define wxListItem_SetStateMask 1756
+#define wxListItem_SetText 1757
+#define wxListItem_SetTextColour 1758
+#define wxListItem_SetWidth 1759
+#define wxListItemAttr_new_0 1760
+#define wxListItemAttr_new_3 1761
+#define wxListItemAttr_GetBackgroundColour 1762
+#define wxListItemAttr_GetFont 1763
+#define wxListItemAttr_GetTextColour 1764
+#define wxListItemAttr_HasBackgroundColour 1765
+#define wxListItemAttr_HasFont 1766
+#define wxListItemAttr_HasTextColour 1767
+#define wxListItemAttr_SetBackgroundColour 1768
+#define wxListItemAttr_SetFont 1769
+#define wxListItemAttr_SetTextColour 1770
+#define wxListItemAttr_destroy 1771
+#define wxImageList_new_0 1772
+#define wxImageList_new_3 1773
+#define wxImageList_Add_1 1774
+#define wxImageList_Add_2_0 1775
+#define wxImageList_Add_2_1 1776
+#define wxImageList_Create 1777
+#define wxImageList_Draw 1779
+#define wxImageList_GetBitmap 1780
+#define wxImageList_GetIcon 1781
+#define wxImageList_GetImageCount 1782
+#define wxImageList_GetSize 1783
+#define wxImageList_Remove 1784
+#define wxImageList_RemoveAll 1785
+#define wxImageList_Replace_2 1786
+#define wxImageList_Replace_3 1787
+#define wxImageList_destroy 1788
+#define wxTextAttr_new_0 1789
+#define wxTextAttr_new_2 1790
+#define wxTextAttr_GetAlignment 1791
+#define wxTextAttr_GetBackgroundColour 1792
+#define wxTextAttr_GetFont 1793
+#define wxTextAttr_GetLeftIndent 1794
+#define wxTextAttr_GetLeftSubIndent 1795
+#define wxTextAttr_GetRightIndent 1796
+#define wxTextAttr_GetTabs 1797
+#define wxTextAttr_GetTextColour 1798
+#define wxTextAttr_HasBackgroundColour 1799
+#define wxTextAttr_HasFont 1800
+#define wxTextAttr_HasTextColour 1801
+#define wxTextAttr_GetFlags 1802
+#define wxTextAttr_IsDefault 1803
+#define wxTextAttr_SetAlignment 1804
+#define wxTextAttr_SetBackgroundColour 1805
+#define wxTextAttr_SetFlags 1806
+#define wxTextAttr_SetFont 1807
+#define wxTextAttr_SetLeftIndent 1808
+#define wxTextAttr_SetRightIndent 1809
+#define wxTextAttr_SetTabs 1810
+#define wxTextAttr_SetTextColour 1811
+#define wxTextAttr_destroy 1812
+#define wxTextCtrl_new_3 1814
+#define wxTextCtrl_new_0 1815
+#define wxTextCtrl_destruct 1817
+#define wxTextCtrl_AppendText 1818
+#define wxTextCtrl_CanCopy 1819
+#define wxTextCtrl_CanCut 1820
+#define wxTextCtrl_CanPaste 1821
+#define wxTextCtrl_CanRedo 1822
+#define wxTextCtrl_CanUndo 1823
+#define wxTextCtrl_Clear 1824
+#define wxTextCtrl_Copy 1825
+#define wxTextCtrl_Create 1826
+#define wxTextCtrl_Cut 1827
+#define wxTextCtrl_DiscardEdits 1828
+#define wxTextCtrl_ChangeValue 1829
+#define wxTextCtrl_EmulateKeyPress 1830
+#define wxTextCtrl_GetDefaultStyle 1831
+#define wxTextCtrl_GetInsertionPoint 1832
+#define wxTextCtrl_GetLastPosition 1833
+#define wxTextCtrl_GetLineLength 1834
+#define wxTextCtrl_GetLineText 1835
+#define wxTextCtrl_GetNumberOfLines 1836
+#define wxTextCtrl_GetRange 1837
+#define wxTextCtrl_GetSelection 1838
+#define wxTextCtrl_GetStringSelection 1839
+#define wxTextCtrl_GetStyle 1840
+#define wxTextCtrl_GetValue 1841
+#define wxTextCtrl_IsEditable 1842
+#define wxTextCtrl_IsModified 1843
+#define wxTextCtrl_IsMultiLine 1844
+#define wxTextCtrl_IsSingleLine 1845
+#define wxTextCtrl_LoadFile 1846
+#define wxTextCtrl_MarkDirty 1847
+#define wxTextCtrl_Paste 1848
+#define wxTextCtrl_PositionToXY 1849
+#define wxTextCtrl_Redo 1850
+#define wxTextCtrl_Remove 1851
+#define wxTextCtrl_Replace 1852
+#define wxTextCtrl_SaveFile 1853
+#define wxTextCtrl_SetDefaultStyle 1854
+#define wxTextCtrl_SetEditable 1855
+#define wxTextCtrl_SetInsertionPoint 1856
+#define wxTextCtrl_SetInsertionPointEnd 1857
+#define wxTextCtrl_SetMaxLength 1859
+#define wxTextCtrl_SetSelection 1860
+#define wxTextCtrl_SetStyle 1861
+#define wxTextCtrl_SetValue 1862
+#define wxTextCtrl_ShowPosition 1863
+#define wxTextCtrl_Undo 1864
+#define wxTextCtrl_WriteText 1865
+#define wxTextCtrl_XYToPosition 1866
+#define wxNotebook_new_0 1869
+#define wxNotebook_new_3 1870
+#define wxNotebook_destruct 1871
+#define wxNotebook_AddPage 1872
+#define wxNotebook_AdvanceSelection 1873
+#define wxNotebook_AssignImageList 1874
+#define wxNotebook_Create 1875
+#define wxNotebook_DeleteAllPages 1876
+#define wxNotebook_DeletePage 1877
+#define wxNotebook_RemovePage 1878
+#define wxNotebook_GetCurrentPage 1879
+#define wxNotebook_GetImageList 1880
+#define wxNotebook_GetPage 1882
+#define wxNotebook_GetPageCount 1883
+#define wxNotebook_GetPageImage 1884
+#define wxNotebook_GetPageText 1885
+#define wxNotebook_GetRowCount 1886
+#define wxNotebook_GetSelection 1887
+#define wxNotebook_GetThemeBackgroundColour 1888
+#define wxNotebook_HitTest 1890
+#define wxNotebook_InsertPage 1892
+#define wxNotebook_SetImageList 1893
+#define wxNotebook_SetPadding 1894
+#define wxNotebook_SetPageSize 1895
+#define wxNotebook_SetPageImage 1896
+#define wxNotebook_SetPageText 1897
+#define wxNotebook_SetSelection 1898
+#define wxNotebook_ChangeSelection 1899
+#define wxChoicebook_new_0 1900
+#define wxChoicebook_new_3 1901
+#define wxChoicebook_AddPage 1902
+#define wxChoicebook_AdvanceSelection 1903
+#define wxChoicebook_AssignImageList 1904
+#define wxChoicebook_Create 1905
+#define wxChoicebook_DeleteAllPages 1906
+#define wxChoicebook_DeletePage 1907
+#define wxChoicebook_RemovePage 1908
+#define wxChoicebook_GetCurrentPage 1909
+#define wxChoicebook_GetImageList 1910
+#define wxChoicebook_GetPage 1912
+#define wxChoicebook_GetPageCount 1913
+#define wxChoicebook_GetPageImage 1914
+#define wxChoicebook_GetPageText 1915
+#define wxChoicebook_GetSelection 1916
+#define wxChoicebook_HitTest 1917
+#define wxChoicebook_InsertPage 1918
+#define wxChoicebook_SetImageList 1919
+#define wxChoicebook_SetPageSize 1920
+#define wxChoicebook_SetPageImage 1921
+#define wxChoicebook_SetPageText 1922
+#define wxChoicebook_SetSelection 1923
+#define wxChoicebook_ChangeSelection 1924
+#define wxChoicebook_destroy 1925
+#define wxToolbook_new_0 1926
+#define wxToolbook_new_3 1927
+#define wxToolbook_AddPage 1928
+#define wxToolbook_AdvanceSelection 1929
+#define wxToolbook_AssignImageList 1930
+#define wxToolbook_Create 1931
+#define wxToolbook_DeleteAllPages 1932
+#define wxToolbook_DeletePage 1933
+#define wxToolbook_RemovePage 1934
+#define wxToolbook_GetCurrentPage 1935
+#define wxToolbook_GetImageList 1936
+#define wxToolbook_GetPage 1938
+#define wxToolbook_GetPageCount 1939
+#define wxToolbook_GetPageImage 1940
+#define wxToolbook_GetPageText 1941
+#define wxToolbook_GetSelection 1942
+#define wxToolbook_HitTest 1944
+#define wxToolbook_InsertPage 1945
+#define wxToolbook_SetImageList 1946
+#define wxToolbook_SetPageSize 1947
+#define wxToolbook_SetPageImage 1948
+#define wxToolbook_SetPageText 1949
+#define wxToolbook_SetSelection 1950
+#define wxToolbook_ChangeSelection 1951
+#define wxToolbook_destroy 1952
+#define wxListbook_new_0 1953
+#define wxListbook_new_3 1954
+#define wxListbook_AddPage 1955
+#define wxListbook_AdvanceSelection 1956
+#define wxListbook_AssignImageList 1957
+#define wxListbook_Create 1958
+#define wxListbook_DeleteAllPages 1959
+#define wxListbook_DeletePage 1960
+#define wxListbook_RemovePage 1961
+#define wxListbook_GetCurrentPage 1962
+#define wxListbook_GetImageList 1963
+#define wxListbook_GetPage 1965
+#define wxListbook_GetPageCount 1966
+#define wxListbook_GetPageImage 1967
+#define wxListbook_GetPageText 1968
+#define wxListbook_GetSelection 1969
+#define wxListbook_HitTest 1971
+#define wxListbook_InsertPage 1972
+#define wxListbook_SetImageList 1973
+#define wxListbook_SetPageSize 1974
+#define wxListbook_SetPageImage 1975
+#define wxListbook_SetPageText 1976
+#define wxListbook_SetSelection 1977
+#define wxListbook_ChangeSelection 1978
+#define wxListbook_destroy 1979
+#define wxTreebook_new_0 1980
+#define wxTreebook_new_3 1981
+#define wxTreebook_AddPage 1982
+#define wxTreebook_AdvanceSelection 1983
+#define wxTreebook_AssignImageList 1984
+#define wxTreebook_Create 1985
+#define wxTreebook_DeleteAllPages 1986
+#define wxTreebook_DeletePage 1987
+#define wxTreebook_RemovePage 1988
+#define wxTreebook_GetCurrentPage 1989
+#define wxTreebook_GetImageList 1990
+#define wxTreebook_GetPage 1992
+#define wxTreebook_GetPageCount 1993
+#define wxTreebook_GetPageImage 1994
+#define wxTreebook_GetPageText 1995
+#define wxTreebook_GetSelection 1996
+#define wxTreebook_ExpandNode 1997
+#define wxTreebook_IsNodeExpanded 1998
+#define wxTreebook_HitTest 2000
+#define wxTreebook_InsertPage 2001
+#define wxTreebook_InsertSubPage 2002
+#define wxTreebook_SetImageList 2003
+#define wxTreebook_SetPageSize 2004
+#define wxTreebook_SetPageImage 2005
+#define wxTreebook_SetPageText 2006
+#define wxTreebook_SetSelection 2007
+#define wxTreebook_ChangeSelection 2008
+#define wxTreebook_destroy 2009
+#define wxTreeCtrl_new_2 2012
+#define wxTreeCtrl_new_0 2013
+#define wxTreeCtrl_destruct 2015
+#define wxTreeCtrl_AddRoot 2016
+#define wxTreeCtrl_AppendItem 2017
+#define wxTreeCtrl_AssignImageList 2018
+#define wxTreeCtrl_AssignStateImageList 2019
+#define wxTreeCtrl_Collapse 2020
+#define wxTreeCtrl_CollapseAndReset 2021
+#define wxTreeCtrl_Create 2022
+#define wxTreeCtrl_Delete 2023
+#define wxTreeCtrl_DeleteAllItems 2024
+#define wxTreeCtrl_DeleteChildren 2025
+#define wxTreeCtrl_EditLabel 2026
+#define wxTreeCtrl_EnsureVisible 2027
+#define wxTreeCtrl_Expand 2028
+#define wxTreeCtrl_GetBoundingRect 2029
+#define wxTreeCtrl_GetChildrenCount 2031
+#define wxTreeCtrl_GetCount 2032
+#define wxTreeCtrl_GetEditControl 2033
+#define wxTreeCtrl_GetFirstChild 2034
+#define wxTreeCtrl_GetNextChild 2035
+#define wxTreeCtrl_GetFirstVisibleItem 2036
+#define wxTreeCtrl_GetImageList 2037
+#define wxTreeCtrl_GetIndent 2038
+#define wxTreeCtrl_GetItemBackgroundColour 2039
+#define wxTreeCtrl_GetItemData 2040
+#define wxTreeCtrl_GetItemFont 2041
+#define wxTreeCtrl_GetItemImage_1 2042
+#define wxTreeCtrl_GetItemImage_2 2043
+#define wxTreeCtrl_GetItemText 2044
+#define wxTreeCtrl_GetItemTextColour 2045
+#define wxTreeCtrl_GetLastChild 2046
+#define wxTreeCtrl_GetNextSibling 2047
+#define wxTreeCtrl_GetNextVisible 2048
+#define wxTreeCtrl_GetItemParent 2049
+#define wxTreeCtrl_GetPrevSibling 2050
+#define wxTreeCtrl_GetPrevVisible 2051
+#define wxTreeCtrl_GetRootItem 2052
+#define wxTreeCtrl_GetSelection 2053
+#define wxTreeCtrl_GetSelections 2054
+#define wxTreeCtrl_GetStateImageList 2055
+#define wxTreeCtrl_HitTest 2056
+#define wxTreeCtrl_InsertItem 2058
+#define wxTreeCtrl_IsBold 2059
+#define wxTreeCtrl_IsExpanded 2060
+#define wxTreeCtrl_IsSelected 2061
+#define wxTreeCtrl_IsVisible 2062
+#define wxTreeCtrl_ItemHasChildren 2063
+#define wxTreeCtrl_IsTreeItemIdOk 2064
+#define wxTreeCtrl_PrependItem 2065
+#define wxTreeCtrl_ScrollTo 2066
+#define wxTreeCtrl_SelectItem_1 2067
+#define wxTreeCtrl_SelectItem_2 2068
+#define wxTreeCtrl_SetIndent 2069
+#define wxTreeCtrl_SetImageList 2070
+#define wxTreeCtrl_SetItemBackgroundColour 2071
+#define wxTreeCtrl_SetItemBold 2072
+#define wxTreeCtrl_SetItemData 2073
+#define wxTreeCtrl_SetItemDropHighlight 2074
+#define wxTreeCtrl_SetItemFont 2075
+#define wxTreeCtrl_SetItemHasChildren 2076
+#define wxTreeCtrl_SetItemImage_2 2077
+#define wxTreeCtrl_SetItemImage_3 2078
+#define wxTreeCtrl_SetItemText 2079
+#define wxTreeCtrl_SetItemTextColour 2080
+#define wxTreeCtrl_SetStateImageList 2081
+#define wxTreeCtrl_SetWindowStyle 2082
+#define wxTreeCtrl_SortChildren 2083
+#define wxTreeCtrl_Toggle 2084
+#define wxTreeCtrl_ToggleItemSelection 2085
+#define wxTreeCtrl_Unselect 2086
+#define wxTreeCtrl_UnselectAll 2087
+#define wxTreeCtrl_UnselectItem 2088
+#define wxScrollBar_new_0 2089
+#define wxScrollBar_new_3 2090
+#define wxScrollBar_destruct 2091
+#define wxScrollBar_Create 2092
+#define wxScrollBar_GetRange 2093
+#define wxScrollBar_GetPageSize 2094
+#define wxScrollBar_GetThumbPosition 2095
+#define wxScrollBar_GetThumbSize 2096
+#define wxScrollBar_SetThumbPosition 2097
+#define wxScrollBar_SetScrollbar 2098
+#define wxSpinButton_new_2 2100
+#define wxSpinButton_new_0 2101
+#define wxSpinButton_Create 2102
+#define wxSpinButton_GetMax 2103
+#define wxSpinButton_GetMin 2104
+#define wxSpinButton_GetValue 2105
+#define wxSpinButton_SetRange 2106
+#define wxSpinButton_SetValue 2107
+#define wxSpinButton_destroy 2108
+#define wxSpinCtrl_new_0 2109
+#define wxSpinCtrl_new_2 2110
+#define wxSpinCtrl_Create 2112
+#define wxSpinCtrl_SetValue_1_1 2115
+#define wxSpinCtrl_SetValue_1_0 2116
+#define wxSpinCtrl_GetValue 2118
+#define wxSpinCtrl_SetRange 2120
+#define wxSpinCtrl_SetSelection 2121
+#define wxSpinCtrl_GetMin 2123
+#define wxSpinCtrl_GetMax 2125
+#define wxSpinCtrl_destroy 2126
+#define wxStaticText_new_0 2127
+#define wxStaticText_new_4 2128
+#define wxStaticText_Create 2129
+#define wxStaticText_GetLabel 2130
+#define wxStaticText_SetLabel 2131
+#define wxStaticText_Wrap 2132
+#define wxStaticText_destroy 2133
+#define wxStaticBitmap_new_0 2134
+#define wxStaticBitmap_new_4 2135
+#define wxStaticBitmap_Create 2136
+#define wxStaticBitmap_GetBitmap 2137
+#define wxStaticBitmap_SetBitmap 2138
+#define wxStaticBitmap_destroy 2139
+#define wxRadioBox_new 2140
+#define wxRadioBox_destruct 2142
+#define wxRadioBox_Create 2143
+#define wxRadioBox_Enable_2 2144
+#define wxRadioBox_Enable_1 2145
+#define wxRadioBox_GetSelection 2146
+#define wxRadioBox_GetString 2147
+#define wxRadioBox_SetSelection 2148
+#define wxRadioBox_Show_2 2149
+#define wxRadioBox_Show_1 2150
+#define wxRadioBox_GetColumnCount 2151
+#define wxRadioBox_GetItemHelpText 2152
+#define wxRadioBox_GetItemToolTip 2153
+#define wxRadioBox_GetItemFromPoint 2155
+#define wxRadioBox_GetRowCount 2156
+#define wxRadioBox_IsItemEnabled 2157
+#define wxRadioBox_IsItemShown 2158
+#define wxRadioBox_SetItemHelpText 2159
+#define wxRadioBox_SetItemToolTip 2160
+#define wxRadioButton_new_0 2161
+#define wxRadioButton_new_4 2162
+#define wxRadioButton_Create 2163
+#define wxRadioButton_GetValue 2164
+#define wxRadioButton_SetValue 2165
+#define wxRadioButton_destroy 2166
+#define wxSlider_new_6 2168
+#define wxSlider_new_0 2169
+#define wxSlider_Create 2170
+#define wxSlider_GetLineSize 2171
+#define wxSlider_GetMax 2172
+#define wxSlider_GetMin 2173
+#define wxSlider_GetPageSize 2174
+#define wxSlider_GetThumbLength 2175
+#define wxSlider_GetValue 2176
+#define wxSlider_SetLineSize 2177
+#define wxSlider_SetPageSize 2178
+#define wxSlider_SetRange 2179
+#define wxSlider_SetThumbLength 2180
+#define wxSlider_SetValue 2181
+#define wxSlider_destroy 2182
+#define wxDialog_new_4 2184
+#define wxDialog_new_0 2185
+#define wxDialog_destruct 2187
+#define wxDialog_Create 2188
+#define wxDialog_CreateButtonSizer 2189
+#define wxDialog_CreateStdDialogButtonSizer 2190
+#define wxDialog_EndModal 2191
+#define wxDialog_GetAffirmativeId 2192
+#define wxDialog_GetReturnCode 2193
+#define wxDialog_IsModal 2194
+#define wxDialog_SetAffirmativeId 2195
+#define wxDialog_SetReturnCode 2196
+#define wxDialog_Show 2197
+#define wxDialog_ShowModal 2198
+#define wxColourDialog_new_0 2199
+#define wxColourDialog_new_2 2200
+#define wxColourDialog_destruct 2201
+#define wxColourDialog_Create 2202
+#define wxColourDialog_GetColourData 2203
+#define wxColourData_new_0 2204
+#define wxColourData_new_1 2205
+#define wxColourData_destruct 2206
+#define wxColourData_GetChooseFull 2207
+#define wxColourData_GetColour 2208
+#define wxColourData_GetCustomColour 2210
+#define wxColourData_SetChooseFull 2211
+#define wxColourData_SetColour 2212
+#define wxColourData_SetCustomColour 2213
+#define wxPalette_new_0 2214
+#define wxPalette_new_4 2215
+#define wxPalette_destruct 2217
+#define wxPalette_Create 2218
+#define wxPalette_GetColoursCount 2219
+#define wxPalette_GetPixel 2220
+#define wxPalette_GetRGB 2221
+#define wxPalette_IsOk 2222
+#define wxDirDialog_new 2226
+#define wxDirDialog_destruct 2227
+#define wxDirDialog_GetPath 2228
+#define wxDirDialog_GetMessage 2229
+#define wxDirDialog_SetMessage 2230
+#define wxDirDialog_SetPath 2231
+#define wxFileDialog_new 2235
+#define wxFileDialog_destruct 2236
+#define wxFileDialog_GetDirectory 2237
+#define wxFileDialog_GetFilename 2238
+#define wxFileDialog_GetFilenames 2239
+#define wxFileDialog_GetFilterIndex 2240
+#define wxFileDialog_GetMessage 2241
+#define wxFileDialog_GetPath 2242
+#define wxFileDialog_GetPaths 2243
+#define wxFileDialog_GetWildcard 2244
+#define wxFileDialog_SetDirectory 2245
+#define wxFileDialog_SetFilename 2246
+#define wxFileDialog_SetFilterIndex 2247
+#define wxFileDialog_SetMessage 2248
+#define wxFileDialog_SetPath 2249
+#define wxFileDialog_SetWildcard 2250
+#define wxPickerBase_SetInternalMargin 2251
+#define wxPickerBase_GetInternalMargin 2252
+#define wxPickerBase_SetTextCtrlProportion 2253
+#define wxPickerBase_SetPickerCtrlProportion 2254
+#define wxPickerBase_GetTextCtrlProportion 2255
+#define wxPickerBase_GetPickerCtrlProportion 2256
+#define wxPickerBase_HasTextCtrl 2257
+#define wxPickerBase_GetTextCtrl 2258
+#define wxPickerBase_IsTextCtrlGrowable 2259
+#define wxPickerBase_SetPickerCtrlGrowable 2260
+#define wxPickerBase_SetTextCtrlGrowable 2261
+#define wxPickerBase_IsPickerCtrlGrowable 2262
+#define wxFilePickerCtrl_new_0 2263
+#define wxFilePickerCtrl_new_3 2264
+#define wxFilePickerCtrl_Create 2265
+#define wxFilePickerCtrl_GetPath 2266
+#define wxFilePickerCtrl_SetPath 2267
+#define wxFilePickerCtrl_destroy 2268
+#define wxDirPickerCtrl_new_0 2269
+#define wxDirPickerCtrl_new_3 2270
+#define wxDirPickerCtrl_Create 2271
+#define wxDirPickerCtrl_GetPath 2272
+#define wxDirPickerCtrl_SetPath 2273
+#define wxDirPickerCtrl_destroy 2274
+#define wxColourPickerCtrl_new_0 2275
+#define wxColourPickerCtrl_new_3 2276
+#define wxColourPickerCtrl_Create 2277
+#define wxColourPickerCtrl_GetColour 2278
+#define wxColourPickerCtrl_SetColour_1_1 2279
+#define wxColourPickerCtrl_SetColour_1_0 2280
+#define wxColourPickerCtrl_destroy 2281
+#define wxDatePickerCtrl_new_0 2282
+#define wxDatePickerCtrl_new_3 2283
+#define wxDatePickerCtrl_GetRange 2284
+#define wxDatePickerCtrl_GetValue 2285
+#define wxDatePickerCtrl_SetRange 2286
+#define wxDatePickerCtrl_SetValue 2287
+#define wxDatePickerCtrl_destroy 2288
+#define wxFontPickerCtrl_new_0 2289
+#define wxFontPickerCtrl_new_3 2290
+#define wxFontPickerCtrl_Create 2291
+#define wxFontPickerCtrl_GetSelectedFont 2292
+#define wxFontPickerCtrl_SetSelectedFont 2293
+#define wxFontPickerCtrl_GetMaxPointSize 2294
+#define wxFontPickerCtrl_SetMaxPointSize 2295
+#define wxFontPickerCtrl_destroy 2296
+#define wxFindReplaceDialog_new_0 2299
+#define wxFindReplaceDialog_new_4 2300
+#define wxFindReplaceDialog_destruct 2301
+#define wxFindReplaceDialog_Create 2302
+#define wxFindReplaceDialog_GetData 2303
+#define wxFindReplaceData_new_0 2304
+#define wxFindReplaceData_new_1 2305
+#define wxFindReplaceData_GetFindString 2306
+#define wxFindReplaceData_GetReplaceString 2307
+#define wxFindReplaceData_GetFlags 2308
+#define wxFindReplaceData_SetFlags 2309
+#define wxFindReplaceData_SetFindString 2310
+#define wxFindReplaceData_SetReplaceString 2311
+#define wxFindReplaceData_destroy 2312
+#define wxMultiChoiceDialog_new_0 2313
+#define wxMultiChoiceDialog_new_5 2315
+#define wxMultiChoiceDialog_GetSelections 2316
+#define wxMultiChoiceDialog_SetSelections 2317
+#define wxMultiChoiceDialog_destroy 2318
+#define wxSingleChoiceDialog_new_0 2319
+#define wxSingleChoiceDialog_new_5 2321
+#define wxSingleChoiceDialog_GetSelection 2322
+#define wxSingleChoiceDialog_GetStringSelection 2323
+#define wxSingleChoiceDialog_SetSelection 2324
+#define wxSingleChoiceDialog_destroy 2325
+#define wxTextEntryDialog_new 2326
+#define wxTextEntryDialog_GetValue 2327
+#define wxTextEntryDialog_SetValue 2328
+#define wxTextEntryDialog_destroy 2329
+#define wxPasswordEntryDialog_new 2330
+#define wxPasswordEntryDialog_destroy 2331
+#define wxFontData_new_0 2332
+#define wxFontData_new_1 2333
+#define wxFontData_destruct 2334
+#define wxFontData_EnableEffects 2335
+#define wxFontData_GetAllowSymbols 2336
+#define wxFontData_GetColour 2337
+#define wxFontData_GetChosenFont 2338
+#define wxFontData_GetEnableEffects 2339
+#define wxFontData_GetInitialFont 2340
+#define wxFontData_GetShowHelp 2341
+#define wxFontData_SetAllowSymbols 2342
+#define wxFontData_SetChosenFont 2343
+#define wxFontData_SetColour 2344
+#define wxFontData_SetInitialFont 2345
+#define wxFontData_SetRange 2346
+#define wxFontData_SetShowHelp 2347
+#define wxFontDialog_new_0 2351
+#define wxFontDialog_new_2 2353
+#define wxFontDialog_Create 2355
+#define wxFontDialog_GetFontData 2356
+#define wxFontDialog_destroy 2358
+#define wxProgressDialog_new 2359
+#define wxProgressDialog_destruct 2360
+#define wxProgressDialog_Resume 2361
+#define wxProgressDialog_Update_2 2362
+#define wxProgressDialog_Update_0 2363
+#define wxMessageDialog_new 2364
+#define wxMessageDialog_destruct 2365
+#define wxPageSetupDialog_new 2366
+#define wxPageSetupDialog_destruct 2367
+#define wxPageSetupDialog_GetPageSetupData 2368
+#define wxPageSetupDialog_ShowModal 2369
+#define wxPageSetupDialogData_new_0 2370
+#define wxPageSetupDialogData_new_1_0 2371
+#define wxPageSetupDialogData_new_1_1 2372
+#define wxPageSetupDialogData_destruct 2373
+#define wxPageSetupDialogData_EnableHelp 2374
+#define wxPageSetupDialogData_EnableMargins 2375
+#define wxPageSetupDialogData_EnableOrientation 2376
+#define wxPageSetupDialogData_EnablePaper 2377
+#define wxPageSetupDialogData_EnablePrinter 2378
+#define wxPageSetupDialogData_GetDefaultMinMargins 2379
+#define wxPageSetupDialogData_GetEnableMargins 2380
+#define wxPageSetupDialogData_GetEnableOrientation 2381
+#define wxPageSetupDialogData_GetEnablePaper 2382
+#define wxPageSetupDialogData_GetEnablePrinter 2383
+#define wxPageSetupDialogData_GetEnableHelp 2384
+#define wxPageSetupDialogData_GetDefaultInfo 2385
+#define wxPageSetupDialogData_GetMarginTopLeft 2386
+#define wxPageSetupDialogData_GetMarginBottomRight 2387
+#define wxPageSetupDialogData_GetMinMarginTopLeft 2388
+#define wxPageSetupDialogData_GetMinMarginBottomRight 2389
+#define wxPageSetupDialogData_GetPaperId 2390
+#define wxPageSetupDialogData_GetPaperSize 2391
+#define wxPageSetupDialogData_GetPrintData 2393
+#define wxPageSetupDialogData_IsOk 2394
+#define wxPageSetupDialogData_SetDefaultInfo 2395
+#define wxPageSetupDialogData_SetDefaultMinMargins 2396
+#define wxPageSetupDialogData_SetMarginTopLeft 2397
+#define wxPageSetupDialogData_SetMarginBottomRight 2398
+#define wxPageSetupDialogData_SetMinMarginTopLeft 2399
+#define wxPageSetupDialogData_SetMinMarginBottomRight 2400
+#define wxPageSetupDialogData_SetPaperId 2401
+#define wxPageSetupDialogData_SetPaperSize_1_1 2402
+#define wxPageSetupDialogData_SetPaperSize_1_0 2403
+#define wxPageSetupDialogData_SetPrintData 2404
+#define wxPrintDialog_new_2_0 2405
+#define wxPrintDialog_new_2_1 2406
+#define wxPrintDialog_destruct 2407
+#define wxPrintDialog_GetPrintDialogData 2408
+#define wxPrintDialog_GetPrintDC 2409
+#define wxPrintDialogData_new_0 2410
+#define wxPrintDialogData_new_1_1 2411
+#define wxPrintDialogData_new_1_0 2412
+#define wxPrintDialogData_destruct 2413
+#define wxPrintDialogData_EnableHelp 2414
+#define wxPrintDialogData_EnablePageNumbers 2415
+#define wxPrintDialogData_EnablePrintToFile 2416
+#define wxPrintDialogData_EnableSelection 2417
+#define wxPrintDialogData_GetAllPages 2418
+#define wxPrintDialogData_GetCollate 2419
+#define wxPrintDialogData_GetFromPage 2420
+#define wxPrintDialogData_GetMaxPage 2421
+#define wxPrintDialogData_GetMinPage 2422
+#define wxPrintDialogData_GetNoCopies 2423
+#define wxPrintDialogData_GetPrintData 2424
+#define wxPrintDialogData_GetPrintToFile 2425
+#define wxPrintDialogData_GetSelection 2426
+#define wxPrintDialogData_GetToPage 2427
+#define wxPrintDialogData_IsOk 2428
+#define wxPrintDialogData_SetCollate 2429
+#define wxPrintDialogData_SetFromPage 2430
+#define wxPrintDialogData_SetMaxPage 2431
+#define wxPrintDialogData_SetMinPage 2432
+#define wxPrintDialogData_SetNoCopies 2433
+#define wxPrintDialogData_SetPrintData 2434
+#define wxPrintDialogData_SetPrintToFile 2435
+#define wxPrintDialogData_SetSelection 2436
+#define wxPrintDialogData_SetToPage 2437
+#define wxPrintData_new_0 2438
+#define wxPrintData_new_1 2439
+#define wxPrintData_destruct 2440
+#define wxPrintData_GetCollate 2441
+#define wxPrintData_GetBin 2442
+#define wxPrintData_GetColour 2443
+#define wxPrintData_GetDuplex 2444
+#define wxPrintData_GetNoCopies 2445
+#define wxPrintData_GetOrientation 2446
+#define wxPrintData_GetPaperId 2447
+#define wxPrintData_GetPrinterName 2448
+#define wxPrintData_GetQuality 2449
+#define wxPrintData_IsOk 2450
+#define wxPrintData_SetBin 2451
+#define wxPrintData_SetCollate 2452
+#define wxPrintData_SetColour 2453
+#define wxPrintData_SetDuplex 2454
+#define wxPrintData_SetNoCopies 2455
+#define wxPrintData_SetOrientation 2456
+#define wxPrintData_SetPaperId 2457
+#define wxPrintData_SetPrinterName 2458
+#define wxPrintData_SetQuality 2459
+#define wxPrintPreview_new_2 2462
+#define wxPrintPreview_new_3 2463
+#define wxPrintPreview_destruct 2465
+#define wxPrintPreview_GetCanvas 2466
+#define wxPrintPreview_GetCurrentPage 2467
+#define wxPrintPreview_GetFrame 2468
+#define wxPrintPreview_GetMaxPage 2469
+#define wxPrintPreview_GetMinPage 2470
+#define wxPrintPreview_GetPrintout 2471
+#define wxPrintPreview_GetPrintoutForPrinting 2472
+#define wxPrintPreview_IsOk 2473
+#define wxPrintPreview_PaintPage 2474
+#define wxPrintPreview_Print 2475
+#define wxPrintPreview_RenderPage 2476
+#define wxPrintPreview_SetCanvas 2477
+#define wxPrintPreview_SetCurrentPage 2478
+#define wxPrintPreview_SetFrame 2479
+#define wxPrintPreview_SetPrintout 2480
+#define wxPrintPreview_SetZoom 2481
+#define wxPreviewFrame_new 2482
+#define wxPreviewFrame_destruct 2483
+#define wxPreviewFrame_CreateControlBar 2484
+#define wxPreviewFrame_CreateCanvas 2485
+#define wxPreviewFrame_Initialize 2486
+#define wxPreviewFrame_OnCloseWindow 2487
+#define wxPreviewControlBar_new 2488
+#define wxPreviewControlBar_destruct 2489
+#define wxPreviewControlBar_CreateButtons 2490
+#define wxPreviewControlBar_GetPrintPreview 2491
+#define wxPreviewControlBar_GetZoomControl 2492
+#define wxPreviewControlBar_SetZoomControl 2493
+#define wxPrinter_new 2495
+#define wxPrinter_CreateAbortWindow 2496
+#define wxPrinter_GetAbort 2497
+#define wxPrinter_GetLastError 2498
+#define wxPrinter_GetPrintDialogData 2499
+#define wxPrinter_Print 2500
+#define wxPrinter_PrintDialog 2501
+#define wxPrinter_ReportError 2502
+#define wxPrinter_Setup 2503
+#define wxPrinter_destroy 2504
+#define wxXmlResource_new_1 2505
+#define wxXmlResource_new_2 2506
+#define wxXmlResource_destruct 2507
+#define wxXmlResource_AttachUnknownControl 2508
+#define wxXmlResource_ClearHandlers 2509
+#define wxXmlResource_CompareVersion 2510
+#define wxXmlResource_Get 2511
+#define wxXmlResource_GetFlags 2512
+#define wxXmlResource_GetVersion 2513
+#define wxXmlResource_GetXRCID 2514
+#define wxXmlResource_InitAllHandlers 2515
+#define wxXmlResource_Load 2516
+#define wxXmlResource_LoadBitmap 2517
+#define wxXmlResource_LoadDialog_2 2518
+#define wxXmlResource_LoadDialog_3 2519
+#define wxXmlResource_LoadFrame_2 2520
+#define wxXmlResource_LoadFrame_3 2521
+#define wxXmlResource_LoadIcon 2522
+#define wxXmlResource_LoadMenu 2523
+#define wxXmlResource_LoadMenuBar_2 2524
+#define wxXmlResource_LoadMenuBar_1 2525
+#define wxXmlResource_LoadPanel_2 2526
+#define wxXmlResource_LoadPanel_3 2527
+#define wxXmlResource_LoadToolBar 2528
+#define wxXmlResource_Set 2529
+#define wxXmlResource_SetFlags 2530
+#define wxXmlResource_Unload 2531
+#define wxXmlResource_xrcctrl 2532
+#define wxHtmlEasyPrinting_new 2533
+#define wxHtmlEasyPrinting_destruct 2534
+#define wxHtmlEasyPrinting_GetPrintData 2535
+#define wxHtmlEasyPrinting_GetPageSetupData 2536
+#define wxHtmlEasyPrinting_PreviewFile 2537
+#define wxHtmlEasyPrinting_PreviewText 2538
+#define wxHtmlEasyPrinting_PrintFile 2539
+#define wxHtmlEasyPrinting_PrintText 2540
+#define wxHtmlEasyPrinting_PageSetup 2541
+#define wxHtmlEasyPrinting_SetFonts 2542
+#define wxHtmlEasyPrinting_SetHeader 2543
+#define wxHtmlEasyPrinting_SetFooter 2544
+#define wxGLCanvas_new_2 2546
+#define wxGLCanvas_new_3_1 2547
+#define wxGLCanvas_new_3_0 2548
+#define wxGLCanvas_GetContext 2549
+#define wxGLCanvas_SetCurrent 2551
+#define wxGLCanvas_SwapBuffers 2552
+#define wxGLCanvas_destroy 2553
+#define wxAuiManager_new 2554
+#define wxAuiManager_destruct 2555
+#define wxAuiManager_AddPane_2_1 2556
+#define wxAuiManager_AddPane_3 2557
+#define wxAuiManager_AddPane_2_0 2558
+#define wxAuiManager_DetachPane 2559
+#define wxAuiManager_GetAllPanes 2560
+#define wxAuiManager_GetArtProvider 2561
+#define wxAuiManager_GetDockSizeConstraint 2562
+#define wxAuiManager_GetFlags 2563
+#define wxAuiManager_GetManagedWindow 2564
+#define wxAuiManager_GetManager 2565
+#define wxAuiManager_GetPane_1_1 2566
+#define wxAuiManager_GetPane_1_0 2567
+#define wxAuiManager_HideHint 2568
+#define wxAuiManager_InsertPane 2569
+#define wxAuiManager_LoadPaneInfo 2570
+#define wxAuiManager_LoadPerspective 2571
+#define wxAuiManager_SavePaneInfo 2572
+#define wxAuiManager_SavePerspective 2573
+#define wxAuiManager_SetArtProvider 2574
+#define wxAuiManager_SetDockSizeConstraint 2575
+#define wxAuiManager_SetFlags 2576
+#define wxAuiManager_SetManagedWindow 2577
+#define wxAuiManager_ShowHint 2578
+#define wxAuiManager_UnInit 2579
+#define wxAuiManager_Update 2580
+#define wxAuiPaneInfo_new_0 2581
+#define wxAuiPaneInfo_new_1 2582
+#define wxAuiPaneInfo_destruct 2583
+#define wxAuiPaneInfo_BestSize_1 2584
+#define wxAuiPaneInfo_BestSize_2 2585
+#define wxAuiPaneInfo_Bottom 2586
+#define wxAuiPaneInfo_BottomDockable 2587
+#define wxAuiPaneInfo_Caption 2588
+#define wxAuiPaneInfo_CaptionVisible 2589
+#define wxAuiPaneInfo_Centre 2590
+#define wxAuiPaneInfo_CentrePane 2591
+#define wxAuiPaneInfo_CloseButton 2592
+#define wxAuiPaneInfo_DefaultPane 2593
+#define wxAuiPaneInfo_DestroyOnClose 2594
+#define wxAuiPaneInfo_Direction 2595
+#define wxAuiPaneInfo_Dock 2596
+#define wxAuiPaneInfo_Dockable 2597
+#define wxAuiPaneInfo_Fixed 2598
+#define wxAuiPaneInfo_Float 2599
+#define wxAuiPaneInfo_Floatable 2600
+#define wxAuiPaneInfo_FloatingPosition_1 2601
+#define wxAuiPaneInfo_FloatingPosition_2 2602
+#define wxAuiPaneInfo_FloatingSize_1 2603
+#define wxAuiPaneInfo_FloatingSize_2 2604
+#define wxAuiPaneInfo_Gripper 2605
+#define wxAuiPaneInfo_GripperTop 2606
+#define wxAuiPaneInfo_HasBorder 2607
+#define wxAuiPaneInfo_HasCaption 2608
+#define wxAuiPaneInfo_HasCloseButton 2609
+#define wxAuiPaneInfo_HasFlag 2610
+#define wxAuiPaneInfo_HasGripper 2611
+#define wxAuiPaneInfo_HasGripperTop 2612
+#define wxAuiPaneInfo_HasMaximizeButton 2613
+#define wxAuiPaneInfo_HasMinimizeButton 2614
+#define wxAuiPaneInfo_HasPinButton 2615
+#define wxAuiPaneInfo_Hide 2616
+#define wxAuiPaneInfo_IsBottomDockable 2617
+#define wxAuiPaneInfo_IsDocked 2618
+#define wxAuiPaneInfo_IsFixed 2619
+#define wxAuiPaneInfo_IsFloatable 2620
+#define wxAuiPaneInfo_IsFloating 2621
+#define wxAuiPaneInfo_IsLeftDockable 2622
+#define wxAuiPaneInfo_IsMovable 2623
+#define wxAuiPaneInfo_IsOk 2624
+#define wxAuiPaneInfo_IsResizable 2625
+#define wxAuiPaneInfo_IsRightDockable 2626
+#define wxAuiPaneInfo_IsShown 2627
+#define wxAuiPaneInfo_IsToolbar 2628
+#define wxAuiPaneInfo_IsTopDockable 2629
+#define wxAuiPaneInfo_Layer 2630
+#define wxAuiPaneInfo_Left 2631
+#define wxAuiPaneInfo_LeftDockable 2632
+#define wxAuiPaneInfo_MaxSize_1 2633
+#define wxAuiPaneInfo_MaxSize_2 2634
+#define wxAuiPaneInfo_MaximizeButton 2635
+#define wxAuiPaneInfo_MinSize_1 2636
+#define wxAuiPaneInfo_MinSize_2 2637
+#define wxAuiPaneInfo_MinimizeButton 2638
+#define wxAuiPaneInfo_Movable 2639
+#define wxAuiPaneInfo_Name 2640
+#define wxAuiPaneInfo_PaneBorder 2641
+#define wxAuiPaneInfo_PinButton 2642
+#define wxAuiPaneInfo_Position 2643
+#define wxAuiPaneInfo_Resizable 2644
+#define wxAuiPaneInfo_Right 2645
+#define wxAuiPaneInfo_RightDockable 2646
+#define wxAuiPaneInfo_Row 2647
+#define wxAuiPaneInfo_SafeSet 2648
+#define wxAuiPaneInfo_SetFlag 2649
+#define wxAuiPaneInfo_Show 2650
+#define wxAuiPaneInfo_ToolbarPane 2651
+#define wxAuiPaneInfo_Top 2652
+#define wxAuiPaneInfo_TopDockable 2653
+#define wxAuiPaneInfo_Window 2654
+#define wxAuiPaneInfo_GetWindow 2655
+#define wxAuiPaneInfo_GetFrame 2656
+#define wxAuiPaneInfo_GetDirection 2657
+#define wxAuiPaneInfo_GetLayer 2658
+#define wxAuiPaneInfo_GetRow 2659
+#define wxAuiPaneInfo_GetPosition 2660
+#define wxAuiPaneInfo_GetFloatingPosition 2661
+#define wxAuiPaneInfo_GetFloatingSize 2662
+#define wxAuiNotebook_new_0 2663
+#define wxAuiNotebook_new_2 2664
+#define wxAuiNotebook_AddPage 2665
+#define wxAuiNotebook_Create 2666
+#define wxAuiNotebook_DeletePage 2667
+#define wxAuiNotebook_GetArtProvider 2668
+#define wxAuiNotebook_GetPage 2669
+#define wxAuiNotebook_GetPageBitmap 2670
+#define wxAuiNotebook_GetPageCount 2671
+#define wxAuiNotebook_GetPageIndex 2672
+#define wxAuiNotebook_GetPageText 2673
+#define wxAuiNotebook_GetSelection 2674
+#define wxAuiNotebook_InsertPage 2675
+#define wxAuiNotebook_RemovePage 2676
+#define wxAuiNotebook_SetArtProvider 2677
+#define wxAuiNotebook_SetFont 2678
+#define wxAuiNotebook_SetPageBitmap 2679
+#define wxAuiNotebook_SetPageText 2680
+#define wxAuiNotebook_SetSelection 2681
+#define wxAuiNotebook_SetTabCtrlHeight 2682
+#define wxAuiNotebook_SetUniformBitmapSize 2683
+#define wxAuiNotebook_destroy 2684
+#define wxAuiTabArt_SetFlags 2685
+#define wxAuiTabArt_SetMeasuringFont 2686
+#define wxAuiTabArt_SetNormalFont 2687
+#define wxAuiTabArt_SetSelectedFont 2688
+#define wxAuiTabArt_SetColour 2689
+#define wxAuiTabArt_SetActiveColour 2690
+#define wxAuiDockArt_GetColour 2691
+#define wxAuiDockArt_GetFont 2692
+#define wxAuiDockArt_GetMetric 2693
+#define wxAuiDockArt_SetColour 2694
+#define wxAuiDockArt_SetFont 2695
+#define wxAuiDockArt_SetMetric 2696
+#define wxAuiSimpleTabArt_new 2697
+#define wxAuiSimpleTabArt_destroy 2698
+#define wxMDIParentFrame_new_0 2699
+#define wxMDIParentFrame_new_4 2700
+#define wxMDIParentFrame_destruct 2701
+#define wxMDIParentFrame_ActivateNext 2702
+#define wxMDIParentFrame_ActivatePrevious 2703
+#define wxMDIParentFrame_ArrangeIcons 2704
+#define wxMDIParentFrame_Cascade 2705
+#define wxMDIParentFrame_Create 2706
+#define wxMDIParentFrame_GetActiveChild 2707
+#define wxMDIParentFrame_GetClientWindow 2708
+#define wxMDIParentFrame_Tile 2709
+#define wxMDIChildFrame_new_0 2710
+#define wxMDIChildFrame_new_4 2711
+#define wxMDIChildFrame_destruct 2712
+#define wxMDIChildFrame_Activate 2713
+#define wxMDIChildFrame_Create 2714
+#define wxMDIChildFrame_Maximize 2715
+#define wxMDIChildFrame_Restore 2716
+#define wxMDIClientWindow_new_0 2717
+#define wxMDIClientWindow_new_2 2718
+#define wxMDIClientWindow_destruct 2719
+#define wxMDIClientWindow_CreateClient 2720
+#define wxLayoutAlgorithm_new 2721
+#define wxLayoutAlgorithm_LayoutFrame 2722
+#define wxLayoutAlgorithm_LayoutMDIFrame 2723
+#define wxLayoutAlgorithm_LayoutWindow 2724
+#define wxLayoutAlgorithm_destroy 2725
+#define wxEvent_GetId 2726
+#define wxEvent_GetSkipped 2727
+#define wxEvent_GetTimestamp 2728
+#define wxEvent_IsCommandEvent 2729
+#define wxEvent_ResumePropagation 2730
+#define wxEvent_ShouldPropagate 2731
+#define wxEvent_Skip 2732
+#define wxEvent_StopPropagation 2733
+#define wxCommandEvent_getClientData 2734
+#define wxCommandEvent_GetExtraLong 2735
+#define wxCommandEvent_GetInt 2736
+#define wxCommandEvent_GetSelection 2737
+#define wxCommandEvent_GetString 2738
+#define wxCommandEvent_IsChecked 2739
+#define wxCommandEvent_IsSelection 2740
+#define wxCommandEvent_SetInt 2741
+#define wxCommandEvent_SetString 2742
+#define wxScrollEvent_GetOrientation 2743
+#define wxScrollEvent_GetPosition 2744
+#define wxScrollWinEvent_GetOrientation 2745
+#define wxScrollWinEvent_GetPosition 2746
+#define wxMouseEvent_AltDown 2747
+#define wxMouseEvent_Button 2748
+#define wxMouseEvent_ButtonDClick 2749
+#define wxMouseEvent_ButtonDown 2750
+#define wxMouseEvent_ButtonUp 2751
+#define wxMouseEvent_CmdDown 2752
+#define wxMouseEvent_ControlDown 2753
+#define wxMouseEvent_Dragging 2754
+#define wxMouseEvent_Entering 2755
+#define wxMouseEvent_GetButton 2756
+#define wxMouseEvent_GetPosition 2759
+#define wxMouseEvent_GetLogicalPosition 2760
+#define wxMouseEvent_GetLinesPerAction 2761
+#define wxMouseEvent_GetWheelRotation 2762
+#define wxMouseEvent_GetWheelDelta 2763
+#define wxMouseEvent_GetX 2764
+#define wxMouseEvent_GetY 2765
+#define wxMouseEvent_IsButton 2766
+#define wxMouseEvent_IsPageScroll 2767
+#define wxMouseEvent_Leaving 2768
+#define wxMouseEvent_LeftDClick 2769
+#define wxMouseEvent_LeftDown 2770
+#define wxMouseEvent_LeftIsDown 2771
+#define wxMouseEvent_LeftUp 2772
+#define wxMouseEvent_MetaDown 2773
+#define wxMouseEvent_MiddleDClick 2774
+#define wxMouseEvent_MiddleDown 2775
+#define wxMouseEvent_MiddleIsDown 2776
+#define wxMouseEvent_MiddleUp 2777
+#define wxMouseEvent_Moving 2778
+#define wxMouseEvent_RightDClick 2779
+#define wxMouseEvent_RightDown 2780
+#define wxMouseEvent_RightIsDown 2781
+#define wxMouseEvent_RightUp 2782
+#define wxMouseEvent_ShiftDown 2783
+#define wxSetCursorEvent_GetCursor 2784
+#define wxSetCursorEvent_GetX 2785
+#define wxSetCursorEvent_GetY 2786
+#define wxSetCursorEvent_HasCursor 2787
+#define wxSetCursorEvent_SetCursor 2788
+#define wxKeyEvent_AltDown 2789
+#define wxKeyEvent_CmdDown 2790
+#define wxKeyEvent_ControlDown 2791
+#define wxKeyEvent_GetKeyCode 2792
+#define wxKeyEvent_GetModifiers 2793
+#define wxKeyEvent_GetPosition 2796
+#define wxKeyEvent_GetRawKeyCode 2797
+#define wxKeyEvent_GetRawKeyFlags 2798
+#define wxKeyEvent_GetUnicodeKey 2799
+#define wxKeyEvent_GetX 2800
+#define wxKeyEvent_GetY 2801
+#define wxKeyEvent_HasModifiers 2802
+#define wxKeyEvent_MetaDown 2803
+#define wxKeyEvent_ShiftDown 2804
+#define wxSizeEvent_GetSize 2805
+#define wxMoveEvent_GetPosition 2806
+#define wxEraseEvent_GetDC 2807
+#define wxFocusEvent_GetWindow 2808
+#define wxChildFocusEvent_GetWindow 2809
+#define wxMenuEvent_GetMenu 2810
+#define wxMenuEvent_GetMenuId 2811
+#define wxMenuEvent_IsPopup 2812
+#define wxCloseEvent_CanVeto 2813
+#define wxCloseEvent_GetLoggingOff 2814
+#define wxCloseEvent_SetCanVeto 2815
+#define wxCloseEvent_SetLoggingOff 2816
+#define wxCloseEvent_Veto 2817
+#define wxShowEvent_SetShow 2818
+#define wxShowEvent_GetShow 2819
+#define wxIconizeEvent_Iconized 2820
+#define wxJoystickEvent_ButtonDown 2821
+#define wxJoystickEvent_ButtonIsDown 2822
+#define wxJoystickEvent_ButtonUp 2823
+#define wxJoystickEvent_GetButtonChange 2824
+#define wxJoystickEvent_GetButtonState 2825
+#define wxJoystickEvent_GetJoystick 2826
+#define wxJoystickEvent_GetPosition 2827
+#define wxJoystickEvent_GetZPosition 2828
+#define wxJoystickEvent_IsButton 2829
+#define wxJoystickEvent_IsMove 2830
+#define wxJoystickEvent_IsZMove 2831
+#define wxUpdateUIEvent_CanUpdate 2832
+#define wxUpdateUIEvent_Check 2833
+#define wxUpdateUIEvent_Enable 2834
+#define wxUpdateUIEvent_Show 2835
+#define wxUpdateUIEvent_GetChecked 2836
+#define wxUpdateUIEvent_GetEnabled 2837
+#define wxUpdateUIEvent_GetShown 2838
+#define wxUpdateUIEvent_GetSetChecked 2839
+#define wxUpdateUIEvent_GetSetEnabled 2840
+#define wxUpdateUIEvent_GetSetShown 2841
+#define wxUpdateUIEvent_GetSetText 2842
+#define wxUpdateUIEvent_GetText 2843
+#define wxUpdateUIEvent_GetMode 2844
+#define wxUpdateUIEvent_GetUpdateInterval 2845
+#define wxUpdateUIEvent_ResetUpdateTime 2846
+#define wxUpdateUIEvent_SetMode 2847
+#define wxUpdateUIEvent_SetText 2848
+#define wxUpdateUIEvent_SetUpdateInterval 2849
+#define wxMouseCaptureChangedEvent_GetCapturedWindow 2850
+#define wxPaletteChangedEvent_SetChangedWindow 2851
+#define wxPaletteChangedEvent_GetChangedWindow 2852
+#define wxQueryNewPaletteEvent_SetPaletteRealized 2853
+#define wxQueryNewPaletteEvent_GetPaletteRealized 2854
+#define wxNavigationKeyEvent_GetDirection 2855
+#define wxNavigationKeyEvent_SetDirection 2856
+#define wxNavigationKeyEvent_IsWindowChange 2857
+#define wxNavigationKeyEvent_SetWindowChange 2858
+#define wxNavigationKeyEvent_IsFromTab 2859
+#define wxNavigationKeyEvent_SetFromTab 2860
+#define wxNavigationKeyEvent_GetCurrentFocus 2861
+#define wxNavigationKeyEvent_SetCurrentFocus 2862
+#define wxHelpEvent_GetOrigin 2863
+#define wxHelpEvent_GetPosition 2864
+#define wxHelpEvent_SetOrigin 2865
+#define wxHelpEvent_SetPosition 2866
+#define wxContextMenuEvent_GetPosition 2867
+#define wxContextMenuEvent_SetPosition 2868
+#define wxIdleEvent_CanSend 2869
+#define wxIdleEvent_GetMode 2870
+#define wxIdleEvent_RequestMore 2871
+#define wxIdleEvent_MoreRequested 2872
+#define wxIdleEvent_SetMode 2873
+#define wxGridEvent_AltDown 2874
+#define wxGridEvent_ControlDown 2875
+#define wxGridEvent_GetCol 2876
+#define wxGridEvent_GetPosition 2877
+#define wxGridEvent_GetRow 2878
+#define wxGridEvent_MetaDown 2879
+#define wxGridEvent_Selecting 2880
+#define wxGridEvent_ShiftDown 2881
+#define wxNotifyEvent_Allow 2882
+#define wxNotifyEvent_IsAllowed 2883
+#define wxNotifyEvent_Veto 2884
+#define wxSashEvent_GetEdge 2885
+#define wxSashEvent_GetDragRect 2886
+#define wxSashEvent_GetDragStatus 2887
+#define wxListEvent_GetCacheFrom 2888
+#define wxListEvent_GetCacheTo 2889
+#define wxListEvent_GetKeyCode 2890
+#define wxListEvent_GetIndex 2891
+#define wxListEvent_GetColumn 2892
+#define wxListEvent_GetPoint 2893
+#define wxListEvent_GetLabel 2894
+#define wxListEvent_GetText 2895
+#define wxListEvent_GetImage 2896
+#define wxListEvent_GetData 2897
+#define wxListEvent_GetMask 2898
+#define wxListEvent_GetItem 2899
+#define wxListEvent_IsEditCancelled 2900
+#define wxDateEvent_GetDate 2901
+#define wxCalendarEvent_GetWeekDay 2902
+#define wxFileDirPickerEvent_GetPath 2903
+#define wxColourPickerEvent_GetColour 2904
+#define wxFontPickerEvent_GetFont 2905
+#define wxStyledTextEvent_GetPosition 2906
+#define wxStyledTextEvent_GetKey 2907
+#define wxStyledTextEvent_GetModifiers 2908
+#define wxStyledTextEvent_GetModificationType 2909
+#define wxStyledTextEvent_GetText 2910
+#define wxStyledTextEvent_GetLength 2911
+#define wxStyledTextEvent_GetLinesAdded 2912
+#define wxStyledTextEvent_GetLine 2913
+#define wxStyledTextEvent_GetFoldLevelNow 2914
+#define wxStyledTextEvent_GetFoldLevelPrev 2915
+#define wxStyledTextEvent_GetMargin 2916
+#define wxStyledTextEvent_GetMessage 2917
+#define wxStyledTextEvent_GetWParam 2918
+#define wxStyledTextEvent_GetLParam 2919
+#define wxStyledTextEvent_GetListType 2920
+#define wxStyledTextEvent_GetX 2921
+#define wxStyledTextEvent_GetY 2922
+#define wxStyledTextEvent_GetDragText 2923
+#define wxStyledTextEvent_GetDragAllowMove 2924
+#define wxStyledTextEvent_GetDragResult 2925
+#define wxStyledTextEvent_GetShift 2926
+#define wxStyledTextEvent_GetControl 2927
+#define wxStyledTextEvent_GetAlt 2928
+#define utils_wxGetKeyState 2929
+#define utils_wxGetMousePosition 2930
+#define utils_wxGetMouseState 2931
+#define utils_wxSetDetectableAutoRepeat 2932
+#define utils_wxBell 2933
+#define utils_wxFindMenuItemId 2934
+#define utils_wxGenericFindWindowAtPoint 2935
+#define utils_wxFindWindowAtPoint 2936
+#define utils_wxBeginBusyCursor 2937
+#define utils_wxEndBusyCursor 2938
+#define utils_wxIsBusy 2939
+#define utils_wxShutdown 2940
+#define utils_wxShell 2941
+#define utils_wxLaunchDefaultBrowser 2942
+#define utils_wxGetEmailAddress 2943
+#define utils_wxGetUserId 2944
+#define utils_wxGetHomeDir 2945
+#define utils_wxNewId 2946
+#define utils_wxRegisterId 2947
+#define utils_wxGetCurrentId 2948
+#define utils_wxGetOsDescription 2949
+#define utils_wxIsPlatformLittleEndian 2950
+#define utils_wxIsPlatform64Bit 2951
+#define gdicmn_wxDisplaySize 2952
+#define gdicmn_wxSetCursor 2953
+#define wxPrintout_new 2954
+#define wxPrintout_destruct 2955
+#define wxPrintout_GetDC 2956
+#define wxPrintout_GetPageSizeMM 2957
+#define wxPrintout_GetPageSizePixels 2958
+#define wxPrintout_GetPaperRectPixels 2959
+#define wxPrintout_GetPPIPrinter 2960
+#define wxPrintout_GetPPIScreen 2961
+#define wxPrintout_GetTitle 2962
+#define wxPrintout_IsPreview 2963
+#define wxPrintout_FitThisSizeToPaper 2964
+#define wxPrintout_FitThisSizeToPage 2965
+#define wxPrintout_FitThisSizeToPageMargins 2966
+#define wxPrintout_MapScreenSizeToPaper 2967
+#define wxPrintout_MapScreenSizeToPage 2968
+#define wxPrintout_MapScreenSizeToPageMargins 2969
+#define wxPrintout_MapScreenSizeToDevice 2970
+#define wxPrintout_GetLogicalPaperRect 2971
+#define wxPrintout_GetLogicalPageRect 2972
+#define wxPrintout_GetLogicalPageMarginsRect 2973
+#define wxPrintout_SetLogicalOrigin 2974
+#define wxPrintout_OffsetLogicalOrigin 2975
+#define wxStyledTextCtrl_new_2 2976
+#define wxStyledTextCtrl_new_0 2977
+#define wxStyledTextCtrl_destruct 2978
+#define wxStyledTextCtrl_Create 2979
+#define wxStyledTextCtrl_AddText 2980
+#define wxStyledTextCtrl_AddStyledText 2981
+#define wxStyledTextCtrl_InsertText 2982
+#define wxStyledTextCtrl_ClearAll 2983
+#define wxStyledTextCtrl_ClearDocumentStyle 2984
+#define wxStyledTextCtrl_GetLength 2985
+#define wxStyledTextCtrl_GetCharAt 2986
+#define wxStyledTextCtrl_GetCurrentPos 2987
+#define wxStyledTextCtrl_GetAnchor 2988
+#define wxStyledTextCtrl_GetStyleAt 2989
+#define wxStyledTextCtrl_Redo 2990
+#define wxStyledTextCtrl_SetUndoCollection 2991
+#define wxStyledTextCtrl_SelectAll 2992
+#define wxStyledTextCtrl_SetSavePoint 2993
+#define wxStyledTextCtrl_GetStyledText 2994
+#define wxStyledTextCtrl_CanRedo 2995
+#define wxStyledTextCtrl_MarkerLineFromHandle 2996
+#define wxStyledTextCtrl_MarkerDeleteHandle 2997
+#define wxStyledTextCtrl_GetUndoCollection 2998
+#define wxStyledTextCtrl_GetViewWhiteSpace 2999
+#define wxStyledTextCtrl_SetViewWhiteSpace 3000
+#define wxStyledTextCtrl_PositionFromPoint 3001
+#define wxStyledTextCtrl_PositionFromPointClose 3002
+#define wxStyledTextCtrl_GotoLine 3003
+#define wxStyledTextCtrl_GotoPos 3004
+#define wxStyledTextCtrl_SetAnchor 3005
+#define wxStyledTextCtrl_GetCurLine 3006
+#define wxStyledTextCtrl_GetEndStyled 3007
+#define wxStyledTextCtrl_ConvertEOLs 3008
+#define wxStyledTextCtrl_GetEOLMode 3009
+#define wxStyledTextCtrl_SetEOLMode 3010
+#define wxStyledTextCtrl_StartStyling 3011
+#define wxStyledTextCtrl_SetStyling 3012
+#define wxStyledTextCtrl_GetBufferedDraw 3013
+#define wxStyledTextCtrl_SetBufferedDraw 3014
+#define wxStyledTextCtrl_SetTabWidth 3015
+#define wxStyledTextCtrl_GetTabWidth 3016
+#define wxStyledTextCtrl_SetCodePage 3017
+#define wxStyledTextCtrl_MarkerDefine 3018
+#define wxStyledTextCtrl_MarkerSetForeground 3019
+#define wxStyledTextCtrl_MarkerSetBackground 3020
+#define wxStyledTextCtrl_MarkerAdd 3021
+#define wxStyledTextCtrl_MarkerDelete 3022
+#define wxStyledTextCtrl_MarkerDeleteAll 3023
+#define wxStyledTextCtrl_MarkerGet 3024
+#define wxStyledTextCtrl_MarkerNext 3025
+#define wxStyledTextCtrl_MarkerPrevious 3026
+#define wxStyledTextCtrl_MarkerDefineBitmap 3027
+#define wxStyledTextCtrl_MarkerAddSet 3028
+#define wxStyledTextCtrl_MarkerSetAlpha 3029
+#define wxStyledTextCtrl_SetMarginType 3030
+#define wxStyledTextCtrl_GetMarginType 3031
+#define wxStyledTextCtrl_SetMarginWidth 3032
+#define wxStyledTextCtrl_GetMarginWidth 3033
+#define wxStyledTextCtrl_SetMarginMask 3034
+#define wxStyledTextCtrl_GetMarginMask 3035
+#define wxStyledTextCtrl_SetMarginSensitive 3036
+#define wxStyledTextCtrl_GetMarginSensitive 3037
+#define wxStyledTextCtrl_StyleClearAll 3038
+#define wxStyledTextCtrl_StyleSetForeground 3039
+#define wxStyledTextCtrl_StyleSetBackground 3040
+#define wxStyledTextCtrl_StyleSetBold 3041
+#define wxStyledTextCtrl_StyleSetItalic 3042
+#define wxStyledTextCtrl_StyleSetSize 3043
+#define wxStyledTextCtrl_StyleSetFaceName 3044
+#define wxStyledTextCtrl_StyleSetEOLFilled 3045
+#define wxStyledTextCtrl_StyleResetDefault 3046
+#define wxStyledTextCtrl_StyleSetUnderline 3047
+#define wxStyledTextCtrl_StyleSetCase 3048
+#define wxStyledTextCtrl_StyleSetHotSpot 3049
+#define wxStyledTextCtrl_SetSelForeground 3050
+#define wxStyledTextCtrl_SetSelBackground 3051
+#define wxStyledTextCtrl_GetSelAlpha 3052
+#define wxStyledTextCtrl_SetSelAlpha 3053
+#define wxStyledTextCtrl_SetCaretForeground 3054
+#define wxStyledTextCtrl_CmdKeyAssign 3055
+#define wxStyledTextCtrl_CmdKeyClear 3056
+#define wxStyledTextCtrl_CmdKeyClearAll 3057
+#define wxStyledTextCtrl_SetStyleBytes 3058
+#define wxStyledTextCtrl_StyleSetVisible 3059
+#define wxStyledTextCtrl_GetCaretPeriod 3060
+#define wxStyledTextCtrl_SetCaretPeriod 3061
+#define wxStyledTextCtrl_SetWordChars 3062
+#define wxStyledTextCtrl_BeginUndoAction 3063
+#define wxStyledTextCtrl_EndUndoAction 3064
+#define wxStyledTextCtrl_IndicatorSetStyle 3065
+#define wxStyledTextCtrl_IndicatorGetStyle 3066
+#define wxStyledTextCtrl_IndicatorSetForeground 3067
+#define wxStyledTextCtrl_IndicatorGetForeground 3068
+#define wxStyledTextCtrl_SetWhitespaceForeground 3069
+#define wxStyledTextCtrl_SetWhitespaceBackground 3070
+#define wxStyledTextCtrl_GetStyleBits 3071
+#define wxStyledTextCtrl_SetLineState 3072
+#define wxStyledTextCtrl_GetLineState 3073
+#define wxStyledTextCtrl_GetMaxLineState 3074
+#define wxStyledTextCtrl_GetCaretLineVisible 3075
+#define wxStyledTextCtrl_SetCaretLineVisible 3076
+#define wxStyledTextCtrl_GetCaretLineBackground 3077
+#define wxStyledTextCtrl_SetCaretLineBackground 3078
+#define wxStyledTextCtrl_AutoCompShow 3079
+#define wxStyledTextCtrl_AutoCompCancel 3080
+#define wxStyledTextCtrl_AutoCompActive 3081
+#define wxStyledTextCtrl_AutoCompPosStart 3082
+#define wxStyledTextCtrl_AutoCompComplete 3083
+#define wxStyledTextCtrl_AutoCompStops 3084
+#define wxStyledTextCtrl_AutoCompSetSeparator 3085
+#define wxStyledTextCtrl_AutoCompGetSeparator 3086
+#define wxStyledTextCtrl_AutoCompSelect 3087
+#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3088
+#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3089
+#define wxStyledTextCtrl_AutoCompSetFillUps 3090
+#define wxStyledTextCtrl_AutoCompSetChooseSingle 3091
+#define wxStyledTextCtrl_AutoCompGetChooseSingle 3092
+#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3093
+#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3094
+#define wxStyledTextCtrl_UserListShow 3095
+#define wxStyledTextCtrl_AutoCompSetAutoHide 3096
+#define wxStyledTextCtrl_AutoCompGetAutoHide 3097
+#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3098
+#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3099
+#define wxStyledTextCtrl_RegisterImage 3100
+#define wxStyledTextCtrl_ClearRegisteredImages 3101
+#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3102
+#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3103
+#define wxStyledTextCtrl_AutoCompSetMaxWidth 3104
+#define wxStyledTextCtrl_AutoCompGetMaxWidth 3105
+#define wxStyledTextCtrl_AutoCompSetMaxHeight 3106
+#define wxStyledTextCtrl_AutoCompGetMaxHeight 3107
+#define wxStyledTextCtrl_SetIndent 3108
+#define wxStyledTextCtrl_GetIndent 3109
+#define wxStyledTextCtrl_SetUseTabs 3110
+#define wxStyledTextCtrl_GetUseTabs 3111
+#define wxStyledTextCtrl_SetLineIndentation 3112
+#define wxStyledTextCtrl_GetLineIndentation 3113
+#define wxStyledTextCtrl_GetLineIndentPosition 3114
+#define wxStyledTextCtrl_GetColumn 3115
+#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3116
+#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3117
+#define wxStyledTextCtrl_SetIndentationGuides 3118
+#define wxStyledTextCtrl_GetIndentationGuides 3119
+#define wxStyledTextCtrl_SetHighlightGuide 3120
+#define wxStyledTextCtrl_GetHighlightGuide 3121
+#define wxStyledTextCtrl_GetLineEndPosition 3122
+#define wxStyledTextCtrl_GetCodePage 3123
+#define wxStyledTextCtrl_GetCaretForeground 3124
+#define wxStyledTextCtrl_GetReadOnly 3125
+#define wxStyledTextCtrl_SetCurrentPos 3126
+#define wxStyledTextCtrl_SetSelectionStart 3127
+#define wxStyledTextCtrl_GetSelectionStart 3128
+#define wxStyledTextCtrl_SetSelectionEnd 3129
+#define wxStyledTextCtrl_GetSelectionEnd 3130
+#define wxStyledTextCtrl_SetPrintMagnification 3131
+#define wxStyledTextCtrl_GetPrintMagnification 3132
+#define wxStyledTextCtrl_SetPrintColourMode 3133
+#define wxStyledTextCtrl_GetPrintColourMode 3134
+#define wxStyledTextCtrl_FindText 3135
+#define wxStyledTextCtrl_FormatRange 3136
+#define wxStyledTextCtrl_GetFirstVisibleLine 3137
+#define wxStyledTextCtrl_GetLine 3138
+#define wxStyledTextCtrl_GetLineCount 3139
+#define wxStyledTextCtrl_SetMarginLeft 3140
+#define wxStyledTextCtrl_GetMarginLeft 3141
+#define wxStyledTextCtrl_SetMarginRight 3142
+#define wxStyledTextCtrl_GetMarginRight 3143
+#define wxStyledTextCtrl_GetModify 3144
+#define wxStyledTextCtrl_SetSelection 3145
+#define wxStyledTextCtrl_GetSelectedText 3146
+#define wxStyledTextCtrl_GetTextRange 3147
+#define wxStyledTextCtrl_HideSelection 3148
+#define wxStyledTextCtrl_LineFromPosition 3149
+#define wxStyledTextCtrl_PositionFromLine 3150
+#define wxStyledTextCtrl_LineScroll 3151
+#define wxStyledTextCtrl_EnsureCaretVisible 3152
+#define wxStyledTextCtrl_ReplaceSelection 3153
+#define wxStyledTextCtrl_SetReadOnly 3154
+#define wxStyledTextCtrl_CanPaste 3155
+#define wxStyledTextCtrl_CanUndo 3156
+#define wxStyledTextCtrl_EmptyUndoBuffer 3157
+#define wxStyledTextCtrl_Undo 3158
+#define wxStyledTextCtrl_Cut 3159
+#define wxStyledTextCtrl_Copy 3160
+#define wxStyledTextCtrl_Paste 3161
+#define wxStyledTextCtrl_Clear 3162
+#define wxStyledTextCtrl_SetText 3163
+#define wxStyledTextCtrl_GetText 3164
+#define wxStyledTextCtrl_GetTextLength 3165
+#define wxStyledTextCtrl_GetOvertype 3166
+#define wxStyledTextCtrl_SetCaretWidth 3167
+#define wxStyledTextCtrl_GetCaretWidth 3168
+#define wxStyledTextCtrl_SetTargetStart 3169
+#define wxStyledTextCtrl_GetTargetStart 3170
+#define wxStyledTextCtrl_SetTargetEnd 3171
+#define wxStyledTextCtrl_GetTargetEnd 3172
+#define wxStyledTextCtrl_ReplaceTarget 3173
+#define wxStyledTextCtrl_SearchInTarget 3174
+#define wxStyledTextCtrl_SetSearchFlags 3175
+#define wxStyledTextCtrl_GetSearchFlags 3176
+#define wxStyledTextCtrl_CallTipShow 3177
+#define wxStyledTextCtrl_CallTipCancel 3178
+#define wxStyledTextCtrl_CallTipActive 3179
+#define wxStyledTextCtrl_CallTipPosAtStart 3180
+#define wxStyledTextCtrl_CallTipSetHighlight 3181
+#define wxStyledTextCtrl_CallTipSetBackground 3182
+#define wxStyledTextCtrl_CallTipSetForeground 3183
+#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3184
+#define wxStyledTextCtrl_CallTipUseStyle 3185
+#define wxStyledTextCtrl_VisibleFromDocLine 3186
+#define wxStyledTextCtrl_DocLineFromVisible 3187
+#define wxStyledTextCtrl_WrapCount 3188
+#define wxStyledTextCtrl_SetFoldLevel 3189
+#define wxStyledTextCtrl_GetFoldLevel 3190
+#define wxStyledTextCtrl_GetLastChild 3191
+#define wxStyledTextCtrl_GetFoldParent 3192
+#define wxStyledTextCtrl_ShowLines 3193
+#define wxStyledTextCtrl_HideLines 3194
+#define wxStyledTextCtrl_GetLineVisible 3195
+#define wxStyledTextCtrl_SetFoldExpanded 3196
+#define wxStyledTextCtrl_GetFoldExpanded 3197
+#define wxStyledTextCtrl_ToggleFold 3198
+#define wxStyledTextCtrl_EnsureVisible 3199
+#define wxStyledTextCtrl_SetFoldFlags 3200
+#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3201
+#define wxStyledTextCtrl_SetTabIndents 3202
+#define wxStyledTextCtrl_GetTabIndents 3203
+#define wxStyledTextCtrl_SetBackSpaceUnIndents 3204
+#define wxStyledTextCtrl_GetBackSpaceUnIndents 3205
+#define wxStyledTextCtrl_SetMouseDwellTime 3206
+#define wxStyledTextCtrl_GetMouseDwellTime 3207
+#define wxStyledTextCtrl_WordStartPosition 3208
+#define wxStyledTextCtrl_WordEndPosition 3209
+#define wxStyledTextCtrl_SetWrapMode 3210
+#define wxStyledTextCtrl_GetWrapMode 3211
+#define wxStyledTextCtrl_SetWrapVisualFlags 3212
+#define wxStyledTextCtrl_GetWrapVisualFlags 3213
+#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3214
+#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3215
+#define wxStyledTextCtrl_SetWrapStartIndent 3216
+#define wxStyledTextCtrl_GetWrapStartIndent 3217
+#define wxStyledTextCtrl_SetLayoutCache 3218
+#define wxStyledTextCtrl_GetLayoutCache 3219
+#define wxStyledTextCtrl_SetScrollWidth 3220
+#define wxStyledTextCtrl_GetScrollWidth 3221
+#define wxStyledTextCtrl_TextWidth 3222
+#define wxStyledTextCtrl_GetEndAtLastLine 3223
+#define wxStyledTextCtrl_TextHeight 3224
+#define wxStyledTextCtrl_SetUseVerticalScrollBar 3225
+#define wxStyledTextCtrl_GetUseVerticalScrollBar 3226
+#define wxStyledTextCtrl_AppendText 3227
+#define wxStyledTextCtrl_GetTwoPhaseDraw 3228
+#define wxStyledTextCtrl_SetTwoPhaseDraw 3229
+#define wxStyledTextCtrl_TargetFromSelection 3230
+#define wxStyledTextCtrl_LinesJoin 3231
+#define wxStyledTextCtrl_LinesSplit 3232
+#define wxStyledTextCtrl_SetFoldMarginColour 3233
+#define wxStyledTextCtrl_SetFoldMarginHiColour 3234
+#define wxStyledTextCtrl_LineDown 3235
+#define wxStyledTextCtrl_LineDownExtend 3236
+#define wxStyledTextCtrl_LineUp 3237
+#define wxStyledTextCtrl_LineUpExtend 3238
+#define wxStyledTextCtrl_CharLeft 3239
+#define wxStyledTextCtrl_CharLeftExtend 3240
+#define wxStyledTextCtrl_CharRight 3241
+#define wxStyledTextCtrl_CharRightExtend 3242
+#define wxStyledTextCtrl_WordLeft 3243
+#define wxStyledTextCtrl_WordLeftExtend 3244
+#define wxStyledTextCtrl_WordRight 3245
+#define wxStyledTextCtrl_WordRightExtend 3246
+#define wxStyledTextCtrl_Home 3247
+#define wxStyledTextCtrl_HomeExtend 3248
+#define wxStyledTextCtrl_LineEnd 3249
+#define wxStyledTextCtrl_LineEndExtend 3250
+#define wxStyledTextCtrl_DocumentStart 3251
+#define wxStyledTextCtrl_DocumentStartExtend 3252
+#define wxStyledTextCtrl_DocumentEnd 3253
+#define wxStyledTextCtrl_DocumentEndExtend 3254
+#define wxStyledTextCtrl_PageUp 3255
+#define wxStyledTextCtrl_PageUpExtend 3256
+#define wxStyledTextCtrl_PageDown 3257
+#define wxStyledTextCtrl_PageDownExtend 3258
+#define wxStyledTextCtrl_EditToggleOvertype 3259
+#define wxStyledTextCtrl_Cancel 3260
+#define wxStyledTextCtrl_DeleteBack 3261
+#define wxStyledTextCtrl_Tab 3262
+#define wxStyledTextCtrl_BackTab 3263
+#define wxStyledTextCtrl_NewLine 3264
+#define wxStyledTextCtrl_FormFeed 3265
+#define wxStyledTextCtrl_VCHome 3266
+#define wxStyledTextCtrl_VCHomeExtend 3267
+#define wxStyledTextCtrl_ZoomIn 3268
+#define wxStyledTextCtrl_ZoomOut 3269
+#define wxStyledTextCtrl_DelWordLeft 3270
+#define wxStyledTextCtrl_DelWordRight 3271
+#define wxStyledTextCtrl_LineCut 3272
+#define wxStyledTextCtrl_LineDelete 3273
+#define wxStyledTextCtrl_LineTranspose 3274
+#define wxStyledTextCtrl_LineDuplicate 3275
+#define wxStyledTextCtrl_LowerCase 3276
+#define wxStyledTextCtrl_UpperCase 3277
+#define wxStyledTextCtrl_LineScrollDown 3278
+#define wxStyledTextCtrl_LineScrollUp 3279
+#define wxStyledTextCtrl_DeleteBackNotLine 3280
+#define wxStyledTextCtrl_HomeDisplay 3281
+#define wxStyledTextCtrl_HomeDisplayExtend 3282
+#define wxStyledTextCtrl_LineEndDisplay 3283
+#define wxStyledTextCtrl_LineEndDisplayExtend 3284
+#define wxStyledTextCtrl_HomeWrapExtend 3285
+#define wxStyledTextCtrl_LineEndWrap 3286
+#define wxStyledTextCtrl_LineEndWrapExtend 3287
+#define wxStyledTextCtrl_VCHomeWrap 3288
+#define wxStyledTextCtrl_VCHomeWrapExtend 3289
+#define wxStyledTextCtrl_LineCopy 3290
+#define wxStyledTextCtrl_MoveCaretInsideView 3291
+#define wxStyledTextCtrl_LineLength 3292
+#define wxStyledTextCtrl_BraceHighlight 3293
+#define wxStyledTextCtrl_BraceBadLight 3294
+#define wxStyledTextCtrl_BraceMatch 3295
+#define wxStyledTextCtrl_GetViewEOL 3296
+#define wxStyledTextCtrl_SetViewEOL 3297
+#define wxStyledTextCtrl_SetModEventMask 3298
+#define wxStyledTextCtrl_GetEdgeColumn 3299
+#define wxStyledTextCtrl_SetEdgeColumn 3300
+#define wxStyledTextCtrl_SetEdgeMode 3301
+#define wxStyledTextCtrl_GetEdgeMode 3302
+#define wxStyledTextCtrl_GetEdgeColour 3303
+#define wxStyledTextCtrl_SetEdgeColour 3304
+#define wxStyledTextCtrl_SearchAnchor 3305
+#define wxStyledTextCtrl_SearchNext 3306
+#define wxStyledTextCtrl_SearchPrev 3307
+#define wxStyledTextCtrl_LinesOnScreen 3308
+#define wxStyledTextCtrl_UsePopUp 3309
+#define wxStyledTextCtrl_SelectionIsRectangle 3310
+#define wxStyledTextCtrl_SetZoom 3311
+#define wxStyledTextCtrl_GetZoom 3312
+#define wxStyledTextCtrl_GetModEventMask 3313
+#define wxStyledTextCtrl_SetSTCFocus 3314
+#define wxStyledTextCtrl_GetSTCFocus 3315
+#define wxStyledTextCtrl_SetStatus 3316
+#define wxStyledTextCtrl_GetStatus 3317
+#define wxStyledTextCtrl_SetMouseDownCaptures 3318
+#define wxStyledTextCtrl_GetMouseDownCaptures 3319
+#define wxStyledTextCtrl_SetSTCCursor 3320
+#define wxStyledTextCtrl_GetSTCCursor 3321
+#define wxStyledTextCtrl_SetControlCharSymbol 3322
+#define wxStyledTextCtrl_GetControlCharSymbol 3323
+#define wxStyledTextCtrl_WordPartLeft 3324
+#define wxStyledTextCtrl_WordPartLeftExtend 3325
+#define wxStyledTextCtrl_WordPartRight 3326
+#define wxStyledTextCtrl_WordPartRightExtend 3327
+#define wxStyledTextCtrl_SetVisiblePolicy 3328
+#define wxStyledTextCtrl_DelLineLeft 3329
+#define wxStyledTextCtrl_DelLineRight 3330
+#define wxStyledTextCtrl_GetXOffset 3331
+#define wxStyledTextCtrl_ChooseCaretX 3332
+#define wxStyledTextCtrl_SetXCaretPolicy 3333
+#define wxStyledTextCtrl_SetYCaretPolicy 3334
+#define wxStyledTextCtrl_GetPrintWrapMode 3335
+#define wxStyledTextCtrl_SetHotspotActiveForeground 3336
+#define wxStyledTextCtrl_SetHotspotActiveBackground 3337
+#define wxStyledTextCtrl_SetHotspotActiveUnderline 3338
+#define wxStyledTextCtrl_SetHotspotSingleLine 3339
+#define wxStyledTextCtrl_ParaDownExtend 3340
+#define wxStyledTextCtrl_ParaUp 3341
+#define wxStyledTextCtrl_ParaUpExtend 3342
+#define wxStyledTextCtrl_PositionBefore 3343
+#define wxStyledTextCtrl_PositionAfter 3344
+#define wxStyledTextCtrl_CopyRange 3345
+#define wxStyledTextCtrl_CopyText 3346
+#define wxStyledTextCtrl_SetSelectionMode 3347
+#define wxStyledTextCtrl_GetSelectionMode 3348
+#define wxStyledTextCtrl_LineDownRectExtend 3349
+#define wxStyledTextCtrl_LineUpRectExtend 3350
+#define wxStyledTextCtrl_CharLeftRectExtend 3351
+#define wxStyledTextCtrl_CharRightRectExtend 3352
+#define wxStyledTextCtrl_HomeRectExtend 3353
+#define wxStyledTextCtrl_VCHomeRectExtend 3354
+#define wxStyledTextCtrl_LineEndRectExtend 3355
+#define wxStyledTextCtrl_PageUpRectExtend 3356
+#define wxStyledTextCtrl_PageDownRectExtend 3357
+#define wxStyledTextCtrl_StutteredPageUp 3358
+#define wxStyledTextCtrl_StutteredPageUpExtend 3359
+#define wxStyledTextCtrl_StutteredPageDown 3360
+#define wxStyledTextCtrl_StutteredPageDownExtend 3361
+#define wxStyledTextCtrl_WordLeftEnd 3362
+#define wxStyledTextCtrl_WordLeftEndExtend 3363
+#define wxStyledTextCtrl_WordRightEnd 3364
+#define wxStyledTextCtrl_WordRightEndExtend 3365
+#define wxStyledTextCtrl_SetWhitespaceChars 3366
+#define wxStyledTextCtrl_SetCharsDefault 3367
+#define wxStyledTextCtrl_AutoCompGetCurrent 3368
+#define wxStyledTextCtrl_Allocate 3369
+#define wxStyledTextCtrl_FindColumn 3370
+#define wxStyledTextCtrl_GetCaretSticky 3371
+#define wxStyledTextCtrl_SetCaretSticky 3372
+#define wxStyledTextCtrl_ToggleCaretSticky 3373
+#define wxStyledTextCtrl_SetPasteConvertEndings 3374
+#define wxStyledTextCtrl_GetPasteConvertEndings 3375
+#define wxStyledTextCtrl_SelectionDuplicate 3376
+#define wxStyledTextCtrl_SetCaretLineBackAlpha 3377
+#define wxStyledTextCtrl_GetCaretLineBackAlpha 3378
+#define wxStyledTextCtrl_StartRecord 3379
+#define wxStyledTextCtrl_StopRecord 3380
+#define wxStyledTextCtrl_SetLexer 3381
+#define wxStyledTextCtrl_GetLexer 3382
+#define wxStyledTextCtrl_Colourise 3383
+#define wxStyledTextCtrl_SetProperty 3384
+#define wxStyledTextCtrl_SetKeyWords 3385
+#define wxStyledTextCtrl_SetLexerLanguage 3386
+#define wxStyledTextCtrl_GetProperty 3387
+#define wxStyledTextCtrl_GetStyleBitsNeeded 3388
+#define wxStyledTextCtrl_GetCurrentLine 3389
+#define wxStyledTextCtrl_StyleSetSpec 3390
+#define wxStyledTextCtrl_StyleSetFont 3391
+#define wxStyledTextCtrl_StyleSetFontAttr 3392
+#define wxStyledTextCtrl_StyleSetCharacterSet 3393
+#define wxStyledTextCtrl_StyleSetFontEncoding 3394
+#define wxStyledTextCtrl_CmdKeyExecute 3395
+#define wxStyledTextCtrl_SetMargins 3396
+#define wxStyledTextCtrl_GetSelection 3397
+#define wxStyledTextCtrl_PointFromPosition 3398
+#define wxStyledTextCtrl_ScrollToLine 3399
+#define wxStyledTextCtrl_ScrollToColumn 3400
+#define wxStyledTextCtrl_SetVScrollBar 3401
+#define wxStyledTextCtrl_SetHScrollBar 3402
+#define wxStyledTextCtrl_GetLastKeydownProcessed 3403
+#define wxStyledTextCtrl_SetLastKeydownProcessed 3404
+#define wxStyledTextCtrl_SaveFile 3405
+#define wxStyledTextCtrl_LoadFile 3406
+#define wxStyledTextCtrl_DoDragOver 3407
+#define wxStyledTextCtrl_DoDropText 3408
+#define wxStyledTextCtrl_GetUseAntiAliasing 3409
+#define wxStyledTextCtrl_AddTextRaw 3410
+#define wxStyledTextCtrl_InsertTextRaw 3411
+#define wxStyledTextCtrl_GetCurLineRaw 3412
+#define wxStyledTextCtrl_GetLineRaw 3413
+#define wxStyledTextCtrl_GetSelectedTextRaw 3414
+#define wxStyledTextCtrl_GetTextRangeRaw 3415
+#define wxStyledTextCtrl_SetTextRaw 3416
+#define wxStyledTextCtrl_GetTextRaw 3417
+#define wxStyledTextCtrl_AppendTextRaw 3418
+#define wxArtProvider_GetBitmap 3419
+#define wxArtProvider_GetIcon 3420
+#define wxTreeEvent_GetKeyCode 3421
+#define wxTreeEvent_GetItem 3422
+#define wxTreeEvent_GetKeyEvent 3423
+#define wxTreeEvent_GetLabel 3424
+#define wxTreeEvent_GetOldItem 3425
+#define wxTreeEvent_GetPoint 3426
+#define wxTreeEvent_IsEditCancelled 3427
+#define wxTreeEvent_SetToolTip 3428
+#define wxNotebookEvent_GetOldSelection 3429
+#define wxNotebookEvent_GetSelection 3430
+#define wxNotebookEvent_SetOldSelection 3431
+#define wxNotebookEvent_SetSelection 3432
+#define wxFileDataObject_new 3433
+#define wxFileDataObject_AddFile 3434
+#define wxFileDataObject_GetFilenames 3435
+#define wxFileDataObject_destroy 3436
+#define wxTextDataObject_new 3437
+#define wxTextDataObject_GetTextLength 3438
+#define wxTextDataObject_GetText 3439
+#define wxTextDataObject_SetText 3440
+#define wxTextDataObject_destroy 3441
+#define wxBitmapDataObject_new_1_1 3442
+#define wxBitmapDataObject_new_1_0 3443
+#define wxBitmapDataObject_GetBitmap 3444
+#define wxBitmapDataObject_SetBitmap 3445
+#define wxBitmapDataObject_destroy 3446
+#define wxClipboard_new 3448
+#define wxClipboard_destruct 3449
+#define wxClipboard_AddData 3450
+#define wxClipboard_Clear 3451
+#define wxClipboard_Close 3452
+#define wxClipboard_Flush 3453
+#define wxClipboard_GetData 3454
+#define wxClipboard_IsOpened 3455
+#define wxClipboard_Open 3456
+#define wxClipboard_SetData 3457
+#define wxClipboard_UsePrimarySelection 3459
+#define wxClipboard_IsSupported 3460
+#define wxClipboard_Get 3461
+#define wxSpinEvent_GetPosition 3462
+#define wxSpinEvent_SetPosition 3463
+#define wxSplitterWindow_new_0 3464
+#define wxSplitterWindow_new_2 3465
+#define wxSplitterWindow_destruct 3466
+#define wxSplitterWindow_Create 3467
+#define wxSplitterWindow_GetMinimumPaneSize 3468
+#define wxSplitterWindow_GetSashGravity 3469
+#define wxSplitterWindow_GetSashPosition 3470
+#define wxSplitterWindow_GetSplitMode 3471
+#define wxSplitterWindow_GetWindow1 3472
+#define wxSplitterWindow_GetWindow2 3473
+#define wxSplitterWindow_Initialize 3474
+#define wxSplitterWindow_IsSplit 3475
+#define wxSplitterWindow_ReplaceWindow 3476
+#define wxSplitterWindow_SetSashGravity 3477
+#define wxSplitterWindow_SetSashPosition 3478
+#define wxSplitterWindow_SetSashSize 3479
+#define wxSplitterWindow_SetMinimumPaneSize 3480
+#define wxSplitterWindow_SetSplitMode 3481
+#define wxSplitterWindow_SplitHorizontally 3482
+#define wxSplitterWindow_SplitVertically 3483
+#define wxSplitterWindow_Unsplit 3484
+#define wxSplitterWindow_UpdateSize 3485
+#define wxSplitterEvent_GetSashPosition 3486
+#define wxSplitterEvent_GetX 3487
+#define wxSplitterEvent_GetY 3488
+#define wxSplitterEvent_GetWindowBeingRemoved 3489
+#define wxSplitterEvent_SetSashPosition 3490
+#define wxHtmlWindow_new_0 3491
+#define wxHtmlWindow_new_2 3492
+#define wxHtmlWindow_AppendToPage 3493
+#define wxHtmlWindow_GetOpenedAnchor 3494
+#define wxHtmlWindow_GetOpenedPage 3495
+#define wxHtmlWindow_GetOpenedPageTitle 3496
+#define wxHtmlWindow_GetRelatedFrame 3497
+#define wxHtmlWindow_HistoryBack 3498
+#define wxHtmlWindow_HistoryCanBack 3499
+#define wxHtmlWindow_HistoryCanForward 3500
+#define wxHtmlWindow_HistoryClear 3501
+#define wxHtmlWindow_HistoryForward 3502
+#define wxHtmlWindow_LoadFile 3503
+#define wxHtmlWindow_LoadPage 3504
+#define wxHtmlWindow_SelectAll 3505
+#define wxHtmlWindow_SelectionToText 3506
+#define wxHtmlWindow_SelectLine 3507
+#define wxHtmlWindow_SelectWord 3508
+#define wxHtmlWindow_SetBorders 3509
+#define wxHtmlWindow_SetFonts 3510
+#define wxHtmlWindow_SetPage 3511
+#define wxHtmlWindow_SetRelatedFrame 3512
+#define wxHtmlWindow_SetRelatedStatusBar 3513
+#define wxHtmlWindow_ToText 3514
+#define wxHtmlWindow_destroy 3515
+#define wxHtmlLinkEvent_GetLinkInfo 3516
+#define wxSystemSettings_GetColour 3517
+#define wxSystemSettings_GetFont 3518
+#define wxSystemSettings_GetMetric 3519
+#define wxSystemSettings_GetScreenType 3520
+#define wxSystemOptions_GetOption 3521
+#define wxSystemOptions_GetOptionInt 3522
+#define wxSystemOptions_HasOption 3523
+#define wxSystemOptions_IsFalse 3524
+#define wxSystemOptions_SetOption_2_1 3525
+#define wxSystemOptions_SetOption_2_0 3526
+#define wxAuiNotebookEvent_SetSelection 3527
+#define wxAuiNotebookEvent_GetSelection 3528
+#define wxAuiNotebookEvent_SetOldSelection 3529
+#define wxAuiNotebookEvent_GetOldSelection 3530
+#define wxAuiNotebookEvent_SetDragSource 3531
+#define wxAuiNotebookEvent_GetDragSource 3532
+#define wxAuiManagerEvent_SetManager 3533
+#define wxAuiManagerEvent_GetManager 3534
+#define wxAuiManagerEvent_SetPane 3535
+#define wxAuiManagerEvent_GetPane 3536
+#define wxAuiManagerEvent_SetButton 3537
+#define wxAuiManagerEvent_GetButton 3538
+#define wxAuiManagerEvent_SetDC 3539
+#define wxAuiManagerEvent_GetDC 3540
+#define wxAuiManagerEvent_Veto 3541
+#define wxAuiManagerEvent_GetVeto 3542
+#define wxAuiManagerEvent_SetCanVeto 3543
+#define wxAuiManagerEvent_CanVeto 3544
+#define wxLogNull_new 3545
+#define wxLogNull_destroy 3546
+#define wxTaskBarIcon_new 3547
+#define wxTaskBarIcon_destruct 3548
+#define wxTaskBarIcon_PopupMenu 3549
+#define wxTaskBarIcon_RemoveIcon 3550
+#define wxTaskBarIcon_SetIcon 3551
+#define wxLocale_new_0 3552
+#define wxLocale_new_2 3554
+#define wxLocale_destruct 3555
+#define wxLocale_Init 3557
+#define wxLocale_AddCatalog_1 3558
+#define wxLocale_AddCatalog_3 3559
+#define wxLocale_AddCatalogLookupPathPrefix 3560
+#define wxLocale_GetCanonicalName 3561
+#define wxLocale_GetLanguage 3562
+#define wxLocale_GetLanguageName 3563
+#define wxLocale_GetLocale 3564
+#define wxLocale_GetName 3565
+#define wxLocale_GetString_2 3566
+#define wxLocale_GetString_4 3567
+#define wxLocale_GetHeaderValue 3568
+#define wxLocale_GetSysName 3569
+#define wxLocale_GetSystemEncoding 3570
+#define wxLocale_GetSystemEncodingName 3571
+#define wxLocale_GetSystemLanguage 3572
+#define wxLocale_IsLoaded 3573
+#define wxLocale_IsOk 3574
+#define wxActivateEvent_GetActive 3575
+#define wxPopupWindow_new_2 3577
+#define wxPopupWindow_new_0 3578
+#define wxPopupWindow_destruct 3580
+#define wxPopupWindow_Create 3581
+#define wxPopupWindow_Position 3582
+#define wxPopupTransientWindow_new_0 3583
+#define wxPopupTransientWindow_new_2 3584
+#define wxPopupTransientWindow_destruct 3585
+#define wxPopupTransientWindow_Popup 3586
+#define wxPopupTransientWindow_Dismiss 3587
+#define wxOverlay_new 3588
+#define wxOverlay_destruct 3589
+#define wxOverlay_Reset 3590
+#define wxDCOverlay_new_6 3591
+#define wxDCOverlay_new_2 3592
+#define wxDCOverlay_destruct 3593
+#define wxDCOverlay_Clear 3594
diff --git a/lib/wx/c_src/wxe_helpers.cpp b/lib/wx/c_src/wxe_helpers.cpp
index 76958a346f..4798e605e8 100644
--- a/lib/wx/c_src/wxe_helpers.cpp
+++ b/lib/wx/c_src/wxe_helpers.cpp
@@ -47,8 +47,8 @@ void wxeCommand::Delete()
if(len > 64)
driver_free(buffer);
buffer = NULL;
- op = -1;
}
+ op = -1;
}
/* ****************************************************************************
@@ -226,7 +226,7 @@ unsigned int wxeFifo::Cleanup(unsigned int def)
// Realloced we need to start from the beginning
return 0;
} else {
- return def;
+ return def < cb_start? def : cb_start;
}
}
diff --git a/lib/wx/c_src/wxe_helpers.h b/lib/wx/c_src/wxe_helpers.h
index 3f66b6d97a..70ffccdc13 100644
--- a/lib/wx/c_src/wxe_helpers.h
+++ b/lib/wx/c_src/wxe_helpers.h
@@ -46,7 +46,7 @@ class wxeCommand
wxeCommand();
virtual ~wxeCommand(); // Use Delete()
- wxeCommand * Save() { return this; };
+ wxeCommand * Save(int Op) { op = Op; return this; };
void Delete();
ErlDrvTermData caller;
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index f899839782..175bcfce54 100644
--- a/lib/wx/c_src/wxe_impl.cpp
+++ b/lib/wx/c_src/wxe_impl.cpp
@@ -238,9 +238,10 @@ void WxeApp::dispatch_cmds()
if(wxe_status != WXE_INITIATED)
return;
recurse_level++;
- // fprintf(stderr, "\r\ndispatch_normal %d\r\n", level);fflush(stderr);
+ // fprintf(stderr, "\r\ndispatch_normal %d\r\n", recurse_level);fflush(stderr);
+ wxe_queue->cb_start = 0;
dispatch(wxe_queue);
- // fprintf(stderr, "\r\ndispatch_done \r\n");fflush(stderr);
+ // fprintf(stderr, "\r\ndispatch_done %d\r\n", recurse_level);fflush(stderr);
recurse_level--;
// Cleanup old memenv's and deleted objects
diff --git a/lib/wx/c_src/wxe_main.cpp b/lib/wx/c_src/wxe_main.cpp
index b928eb9567..6fcde42eb5 100644
--- a/lib/wx/c_src/wxe_main.cpp
+++ b/lib/wx/c_src/wxe_main.cpp
@@ -125,8 +125,8 @@ void *wxe_main_loop(void *vpdl)
{
int result;
int argc = 1;
- char * temp = (char *) "Erlang";
- char * argv[] = {temp,NULL};
+ const wxChar temp[10] = L"Erlang";
+ wxChar * argv[] = {(wxChar *)temp, NULL};
ErlDrvPDL pdl = (ErlDrvPDL) vpdl;
driver_pdl_inc_refc(pdl);
diff --git a/lib/wx/examples/demo/ex_canvas.erl b/lib/wx/examples/demo/ex_canvas.erl
index 2a95ea0777..b00ce81993 100644
--- a/lib/wx/examples/demo/ex_canvas.erl
+++ b/lib/wx/examples/demo/ex_canvas.erl
@@ -136,11 +136,14 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked}},
{noreply, State};
handle_event(#wx{event = #wxSize{size={W,H}}},
State = #state{bitmap=Prev, canvas=Canvas}) ->
- Bitmap = wxBitmap:new(W,H),
- draw(Canvas, Bitmap, fun(DC) -> wxDC:clear(DC) end),
- wxBitmap:destroy(Prev),
- {noreply, State#state{bitmap = Bitmap}};
-
+ if W > 0 andalso H > 0 ->
+ Bitmap = wxBitmap:new(W,H),
+ draw(Canvas, Bitmap, fun(DC) -> wxDC:clear(DC) end),
+ wxBitmap:destroy(Prev),
+ {noreply, State#state{bitmap = Bitmap}};
+ true ->
+ {noreply, State}
+ end;
handle_event(#wx{event = #wxMouse{type=left_down, x=X, y=Y}}, State) ->
{noreply, State#state{pos={X,Y}}};
handle_event(#wx{event = #wxMouse{type=motion, x=X1, y=Y1}},
diff --git a/lib/wx/examples/demo/ex_canvas_paint.erl b/lib/wx/examples/demo/ex_canvas_paint.erl
index aa510f5342..75eb840b04 100644
--- a/lib/wx/examples/demo/ex_canvas_paint.erl
+++ b/lib/wx/examples/demo/ex_canvas_paint.erl
@@ -157,10 +157,15 @@ handle_event(#wx{event = #wxMouse{type = motion, x = X, y = Y}},
{noreply, State#state{old_pos = {X,Y}}};
%% Resize event
handle_event(#wx{event = #wxSize{size = {W,H}}}, State = #state{bitmap=Prev}) ->
- wxBitmap:destroy(Prev),
- Bitmap = wxBitmap:new(W,H),
- draw(State#state.canvas, Bitmap, fun(DC) -> wxDC:clear(DC) end),
- {noreply, State#state{bitmap=Bitmap}};
+ case W > 0 andalso H > 0 of
+ true ->
+ wxBitmap:destroy(Prev),
+ Bitmap = wxBitmap:new(W,H),
+ draw(State#state.canvas, Bitmap, fun(DC) -> wxDC:clear(DC) end),
+ {noreply, State#state{bitmap=Bitmap}};
+ false ->
+ {noreply, State}
+ end;
handle_event(#wx{event = #wxMouse{type = left_dclick,x = X,y = Y}}, State = #state{}) ->
wxPanel:connect(State#state.canvas, motion),
{noreply, State#state{old_pos = {X,Y}}};
@@ -235,11 +240,10 @@ draw(Canvas, Bitmap, Fun) ->
CDC = wxClientDC:new(Canvas),
Fun(MemoryDC),
-
wxDC:blit(CDC, {0,0},
{wxBitmap:getWidth(Bitmap), wxBitmap:getHeight(Bitmap)},
MemoryDC, {0,0}),
-
+
wxClientDC:destroy(CDC),
wxMemoryDC:destroy(MemoryDC).
diff --git a/lib/wx/src/gen/wxGauge.erl b/lib/wx/src/gen/wxGauge.erl
index 51c35145c4..2745e251b4 100644
--- a/lib/wx/src/gen/wxGauge.erl
+++ b/lib/wx/src/gen/wxGauge.erl
@@ -30,9 +30,8 @@
-module(wxGauge).
-include("wxe.hrl").
--export([create/4,create/5,destroy/1,getBezelFace/1,getRange/1,getShadowWidth/1,
- getValue/1,isVertical/1,new/0,new/3,new/4,pulse/1,setBezelFace/2,setRange/2,
- setShadowWidth/2,setValue/2]).
+-export([create/4,create/5,destroy/1,getRange/1,getValue/1,isVertical/1,new/0,
+ new/3,new/4,pulse/1,setRange/2,setValue/2]).
%% inherited exports
-export([cacheBestSize/2,canSetTransparent/1,captureMouse/1,center/1,center/2,
@@ -142,14 +141,6 @@ create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Ra
wxe_util:call(?wxGauge_Create,
<<ThisRef:32/?UI,ParentRef:32/?UI,Id:32/?UI,Range:32/?UI, BinOpt/binary>>).
-%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugegetbezelface">external documentation</a>.
--spec getBezelFace(This) -> integer() when
- This::wxGauge().
-getBezelFace(#wx_ref{type=ThisT,ref=ThisRef}) ->
- ?CLASS(ThisT,wxGauge),
- wxe_util:call(?wxGauge_GetBezelFace,
- <<ThisRef:32/?UI>>).
-
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugegetrange">external documentation</a>.
-spec getRange(This) -> integer() when
This::wxGauge().
@@ -158,14 +149,6 @@ getRange(#wx_ref{type=ThisT,ref=ThisRef}) ->
wxe_util:call(?wxGauge_GetRange,
<<ThisRef:32/?UI>>).
-%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugegetshadowwidth">external documentation</a>.
--spec getShadowWidth(This) -> integer() when
- This::wxGauge().
-getShadowWidth(#wx_ref{type=ThisT,ref=ThisRef}) ->
- ?CLASS(ThisT,wxGauge),
- wxe_util:call(?wxGauge_GetShadowWidth,
- <<ThisRef:32/?UI>>).
-
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugegetvalue">external documentation</a>.
-spec getValue(This) -> integer() when
This::wxGauge().
@@ -182,15 +165,6 @@ isVertical(#wx_ref{type=ThisT,ref=ThisRef}) ->
wxe_util:call(?wxGauge_IsVertical,
<<ThisRef:32/?UI>>).
-%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugesetbezelface">external documentation</a>.
--spec setBezelFace(This, W) -> ok when
- This::wxGauge(), W::integer().
-setBezelFace(#wx_ref{type=ThisT,ref=ThisRef},W)
- when is_integer(W) ->
- ?CLASS(ThisT,wxGauge),
- wxe_util:cast(?wxGauge_SetBezelFace,
- <<ThisRef:32/?UI,W:32/?UI>>).
-
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugesetrange">external documentation</a>.
-spec setRange(This, R) -> ok when
This::wxGauge(), R::integer().
@@ -200,15 +174,6 @@ setRange(#wx_ref{type=ThisT,ref=ThisRef},R)
wxe_util:cast(?wxGauge_SetRange,
<<ThisRef:32/?UI,R:32/?UI>>).
-%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugesetshadowwidth">external documentation</a>.
--spec setShadowWidth(This, W) -> ok when
- This::wxGauge(), W::integer().
-setShadowWidth(#wx_ref{type=ThisT,ref=ThisRef},W)
- when is_integer(W) ->
- ?CLASS(ThisT,wxGauge),
- wxe_util:cast(?wxGauge_SetShadowWidth,
- <<ThisRef:32/?UI,W:32/?UI>>).
-
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgauge.html#wxgaugesetvalue">external documentation</a>.
-spec setValue(This, Pos) -> ok when
This::wxGauge(), Pos::integer().
diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl
index 2ec485cd52..78c6577439 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -1457,1925 +1457,1921 @@ wxdebug_table() ->
{1602, {wxGauge, new_0, 0}},
{1603, {wxGauge, new_4, 4}},
{1604, {wxGauge, create, 4}},
- {1605, {wxGauge, getBezelFace, 0}},
- {1606, {wxGauge, getRange, 0}},
- {1607, {wxGauge, getShadowWidth, 0}},
- {1608, {wxGauge, getValue, 0}},
- {1609, {wxGauge, isVertical, 0}},
- {1610, {wxGauge, setBezelFace, 1}},
- {1611, {wxGauge, setRange, 1}},
- {1612, {wxGauge, setShadowWidth, 1}},
- {1613, {wxGauge, setValue, 1}},
- {1614, {wxGauge, pulse, 0}},
- {1615, {wxGauge, 'Destroy', undefined}},
- {1616, {wxGenericDirCtrl, new_0, 0}},
- {1617, {wxGenericDirCtrl, new_2, 2}},
- {1618, {wxGenericDirCtrl, destruct, 0}},
- {1619, {wxGenericDirCtrl, create, 2}},
- {1620, {wxGenericDirCtrl, init, 0}},
- {1621, {wxGenericDirCtrl, collapseTree, 0}},
- {1622, {wxGenericDirCtrl, expandPath, 1}},
- {1623, {wxGenericDirCtrl, getDefaultPath, 0}},
- {1624, {wxGenericDirCtrl, getPath, 0}},
- {1625, {wxGenericDirCtrl, getFilePath, 0}},
- {1626, {wxGenericDirCtrl, getFilter, 0}},
- {1627, {wxGenericDirCtrl, getFilterIndex, 0}},
- {1628, {wxGenericDirCtrl, getRootId, 0}},
- {1629, {wxGenericDirCtrl, getTreeCtrl, 0}},
- {1630, {wxGenericDirCtrl, reCreateTree, 0}},
- {1631, {wxGenericDirCtrl, setDefaultPath, 1}},
- {1632, {wxGenericDirCtrl, setFilter, 1}},
- {1633, {wxGenericDirCtrl, setFilterIndex, 1}},
- {1634, {wxGenericDirCtrl, setPath, 1}},
- {1636, {wxStaticBox, new_4, 4}},
- {1637, {wxStaticBox, new_0, 0}},
- {1638, {wxStaticBox, create, 4}},
- {1639, {wxStaticBox, 'Destroy', undefined}},
- {1641, {wxStaticLine, new_2, 2}},
- {1642, {wxStaticLine, new_0, 0}},
- {1643, {wxStaticLine, create, 2}},
- {1644, {wxStaticLine, isVertical, 0}},
- {1645, {wxStaticLine, getDefaultSize, 0}},
- {1646, {wxStaticLine, 'Destroy', undefined}},
- {1649, {wxListBox, new_3, 3}},
- {1650, {wxListBox, new_0, 0}},
- {1652, {wxListBox, destruct, 0}},
- {1654, {wxListBox, create, 6}},
- {1655, {wxListBox, deselect, 1}},
- {1656, {wxListBox, getSelections, 1}},
- {1657, {wxListBox, insertItems, 2}},
- {1658, {wxListBox, isSelected, 1}},
- {1659, {wxListBox, set, 1}},
- {1660, {wxListBox, hitTest, 1}},
- {1661, {wxListBox, setFirstItem_1_0, 1}},
- {1662, {wxListBox, setFirstItem_1_1, 1}},
- {1663, {wxListCtrl, new_0, 0}},
- {1664, {wxListCtrl, new_2, 2}},
- {1665, {wxListCtrl, arrange, 1}},
- {1666, {wxListCtrl, assignImageList, 2}},
- {1667, {wxListCtrl, clearAll, 0}},
- {1668, {wxListCtrl, create, 2}},
- {1669, {wxListCtrl, deleteAllItems, 0}},
- {1670, {wxListCtrl, deleteColumn, 1}},
- {1671, {wxListCtrl, deleteItem, 1}},
- {1672, {wxListCtrl, editLabel, 1}},
- {1673, {wxListCtrl, ensureVisible, 1}},
- {1674, {wxListCtrl, findItem_3_0, 3}},
- {1675, {wxListCtrl, findItem_3_1, 3}},
- {1676, {wxListCtrl, getColumn, 2}},
- {1677, {wxListCtrl, getColumnCount, 0}},
- {1678, {wxListCtrl, getColumnWidth, 1}},
- {1679, {wxListCtrl, getCountPerPage, 0}},
- {1680, {wxListCtrl, getEditControl, 0}},
- {1681, {wxListCtrl, getImageList, 1}},
- {1682, {wxListCtrl, getItem, 1}},
- {1683, {wxListCtrl, getItemBackgroundColour, 1}},
- {1684, {wxListCtrl, getItemCount, 0}},
- {1685, {wxListCtrl, getItemData, 1}},
- {1686, {wxListCtrl, getItemFont, 1}},
- {1687, {wxListCtrl, getItemPosition, 2}},
- {1688, {wxListCtrl, getItemRect, 3}},
- {1689, {wxListCtrl, getItemSpacing, 0}},
- {1690, {wxListCtrl, getItemState, 2}},
- {1691, {wxListCtrl, getItemText, 1}},
- {1692, {wxListCtrl, getItemTextColour, 1}},
- {1693, {wxListCtrl, getNextItem, 2}},
- {1694, {wxListCtrl, getSelectedItemCount, 0}},
- {1695, {wxListCtrl, getTextColour, 0}},
- {1696, {wxListCtrl, getTopItem, 0}},
- {1697, {wxListCtrl, getViewRect, 0}},
- {1698, {wxListCtrl, hitTest, 3}},
- {1699, {wxListCtrl, insertColumn_2, 2}},
- {1700, {wxListCtrl, insertColumn_3, 3}},
- {1701, {wxListCtrl, insertItem_1, 1}},
- {1702, {wxListCtrl, insertItem_2_1, 2}},
- {1703, {wxListCtrl, insertItem_2_0, 2}},
- {1704, {wxListCtrl, insertItem_3, 3}},
- {1705, {wxListCtrl, refreshItem, 1}},
- {1706, {wxListCtrl, refreshItems, 2}},
- {1707, {wxListCtrl, scrollList, 2}},
- {1708, {wxListCtrl, setBackgroundColour, 1}},
- {1709, {wxListCtrl, setColumn, 2}},
- {1710, {wxListCtrl, setColumnWidth, 2}},
- {1711, {wxListCtrl, setImageList, 2}},
- {1712, {wxListCtrl, setItem_1, 1}},
- {1713, {wxListCtrl, setItem_4, 4}},
- {1714, {wxListCtrl, setItemBackgroundColour, 2}},
- {1715, {wxListCtrl, setItemCount, 1}},
- {1716, {wxListCtrl, setItemData, 2}},
- {1717, {wxListCtrl, setItemFont, 2}},
- {1718, {wxListCtrl, setItemImage, 3}},
- {1719, {wxListCtrl, setItemColumnImage, 3}},
- {1720, {wxListCtrl, setItemPosition, 2}},
- {1721, {wxListCtrl, setItemState, 3}},
- {1722, {wxListCtrl, setItemText, 2}},
- {1723, {wxListCtrl, setItemTextColour, 2}},
- {1724, {wxListCtrl, setSingleStyle, 2}},
- {1725, {wxListCtrl, setTextColour, 1}},
- {1726, {wxListCtrl, setWindowStyleFlag, 1}},
- {1727, {wxListCtrl, sortItems, 2}},
- {1728, {wxListCtrl, 'Destroy', undefined}},
- {1729, {wxListView, clearColumnImage, 1}},
- {1730, {wxListView, focus, 1}},
- {1731, {wxListView, getFirstSelected, 0}},
- {1732, {wxListView, getFocusedItem, 0}},
- {1733, {wxListView, getNextSelected, 1}},
- {1734, {wxListView, isSelected, 1}},
- {1735, {wxListView, select, 2}},
- {1736, {wxListView, setColumnImage, 2}},
- {1737, {wxListItem, new_0, 0}},
- {1738, {wxListItem, new_1, 1}},
- {1739, {wxListItem, destruct, 0}},
- {1740, {wxListItem, clear, 0}},
- {1741, {wxListItem, getAlign, 0}},
- {1742, {wxListItem, getBackgroundColour, 0}},
- {1743, {wxListItem, getColumn, 0}},
- {1744, {wxListItem, getFont, 0}},
- {1745, {wxListItem, getId, 0}},
- {1746, {wxListItem, getImage, 0}},
- {1747, {wxListItem, getMask, 0}},
- {1748, {wxListItem, getState, 0}},
- {1749, {wxListItem, getText, 0}},
- {1750, {wxListItem, getTextColour, 0}},
- {1751, {wxListItem, getWidth, 0}},
- {1752, {wxListItem, setAlign, 1}},
- {1753, {wxListItem, setBackgroundColour, 1}},
- {1754, {wxListItem, setColumn, 1}},
- {1755, {wxListItem, setFont, 1}},
- {1756, {wxListItem, setId, 1}},
- {1757, {wxListItem, setImage, 1}},
- {1758, {wxListItem, setMask, 1}},
- {1759, {wxListItem, setState, 1}},
- {1760, {wxListItem, setStateMask, 1}},
- {1761, {wxListItem, setText, 1}},
- {1762, {wxListItem, setTextColour, 1}},
- {1763, {wxListItem, setWidth, 1}},
- {1764, {wxListItemAttr, new_0, 0}},
- {1765, {wxListItemAttr, new_3, 3}},
- {1766, {wxListItemAttr, getBackgroundColour, 0}},
- {1767, {wxListItemAttr, getFont, 0}},
- {1768, {wxListItemAttr, getTextColour, 0}},
- {1769, {wxListItemAttr, hasBackgroundColour, 0}},
- {1770, {wxListItemAttr, hasFont, 0}},
- {1771, {wxListItemAttr, hasTextColour, 0}},
- {1772, {wxListItemAttr, setBackgroundColour, 1}},
- {1773, {wxListItemAttr, setFont, 1}},
- {1774, {wxListItemAttr, setTextColour, 1}},
- {1775, {wxListItemAttr, 'Destroy', undefined}},
- {1776, {wxImageList, new_0, 0}},
- {1777, {wxImageList, new_3, 3}},
- {1778, {wxImageList, add_1, 1}},
- {1779, {wxImageList, add_2_0, 2}},
- {1780, {wxImageList, add_2_1, 2}},
- {1781, {wxImageList, create, 3}},
- {1783, {wxImageList, draw, 5}},
- {1784, {wxImageList, getBitmap, 1}},
- {1785, {wxImageList, getIcon, 1}},
- {1786, {wxImageList, getImageCount, 0}},
- {1787, {wxImageList, getSize, 3}},
- {1788, {wxImageList, remove, 1}},
- {1789, {wxImageList, removeAll, 0}},
- {1790, {wxImageList, replace_2, 2}},
- {1791, {wxImageList, replace_3, 3}},
- {1792, {wxImageList, 'Destroy', undefined}},
- {1793, {wxTextAttr, new_0, 0}},
- {1794, {wxTextAttr, new_2, 2}},
- {1795, {wxTextAttr, getAlignment, 0}},
- {1796, {wxTextAttr, getBackgroundColour, 0}},
- {1797, {wxTextAttr, getFont, 0}},
- {1798, {wxTextAttr, getLeftIndent, 0}},
- {1799, {wxTextAttr, getLeftSubIndent, 0}},
- {1800, {wxTextAttr, getRightIndent, 0}},
- {1801, {wxTextAttr, getTabs, 0}},
- {1802, {wxTextAttr, getTextColour, 0}},
- {1803, {wxTextAttr, hasBackgroundColour, 0}},
- {1804, {wxTextAttr, hasFont, 0}},
- {1805, {wxTextAttr, hasTextColour, 0}},
- {1806, {wxTextAttr, getFlags, 0}},
- {1807, {wxTextAttr, isDefault, 0}},
- {1808, {wxTextAttr, setAlignment, 1}},
- {1809, {wxTextAttr, setBackgroundColour, 1}},
- {1810, {wxTextAttr, setFlags, 1}},
- {1811, {wxTextAttr, setFont, 2}},
- {1812, {wxTextAttr, setLeftIndent, 2}},
- {1813, {wxTextAttr, setRightIndent, 1}},
- {1814, {wxTextAttr, setTabs, 1}},
- {1815, {wxTextAttr, setTextColour, 1}},
- {1816, {wxTextAttr, 'Destroy', undefined}},
- {1818, {wxTextCtrl, new_3, 3}},
- {1819, {wxTextCtrl, new_0, 0}},
- {1821, {wxTextCtrl, destruct, 0}},
- {1822, {wxTextCtrl, appendText, 1}},
- {1823, {wxTextCtrl, canCopy, 0}},
- {1824, {wxTextCtrl, canCut, 0}},
- {1825, {wxTextCtrl, canPaste, 0}},
- {1826, {wxTextCtrl, canRedo, 0}},
- {1827, {wxTextCtrl, canUndo, 0}},
- {1828, {wxTextCtrl, clear, 0}},
- {1829, {wxTextCtrl, copy, 0}},
- {1830, {wxTextCtrl, create, 3}},
- {1831, {wxTextCtrl, cut, 0}},
- {1832, {wxTextCtrl, discardEdits, 0}},
- {1833, {wxTextCtrl, changeValue, 1}},
- {1834, {wxTextCtrl, emulateKeyPress, 1}},
- {1835, {wxTextCtrl, getDefaultStyle, 0}},
- {1836, {wxTextCtrl, getInsertionPoint, 0}},
- {1837, {wxTextCtrl, getLastPosition, 0}},
- {1838, {wxTextCtrl, getLineLength, 1}},
- {1839, {wxTextCtrl, getLineText, 1}},
- {1840, {wxTextCtrl, getNumberOfLines, 0}},
- {1841, {wxTextCtrl, getRange, 2}},
- {1842, {wxTextCtrl, getSelection, 2}},
- {1843, {wxTextCtrl, getStringSelection, 0}},
- {1844, {wxTextCtrl, getStyle, 2}},
- {1845, {wxTextCtrl, getValue, 0}},
- {1846, {wxTextCtrl, isEditable, 0}},
- {1847, {wxTextCtrl, isModified, 0}},
- {1848, {wxTextCtrl, isMultiLine, 0}},
- {1849, {wxTextCtrl, isSingleLine, 0}},
- {1850, {wxTextCtrl, loadFile, 2}},
- {1851, {wxTextCtrl, markDirty, 0}},
- {1852, {wxTextCtrl, paste, 0}},
- {1853, {wxTextCtrl, positionToXY, 3}},
- {1854, {wxTextCtrl, redo, 0}},
- {1855, {wxTextCtrl, remove, 2}},
- {1856, {wxTextCtrl, replace, 3}},
- {1857, {wxTextCtrl, saveFile, 1}},
- {1858, {wxTextCtrl, setDefaultStyle, 1}},
- {1859, {wxTextCtrl, setEditable, 1}},
- {1860, {wxTextCtrl, setInsertionPoint, 1}},
- {1861, {wxTextCtrl, setInsertionPointEnd, 0}},
- {1863, {wxTextCtrl, setMaxLength, 1}},
- {1864, {wxTextCtrl, setSelection, 2}},
- {1865, {wxTextCtrl, setStyle, 3}},
- {1866, {wxTextCtrl, setValue, 1}},
- {1867, {wxTextCtrl, showPosition, 1}},
- {1868, {wxTextCtrl, undo, 0}},
- {1869, {wxTextCtrl, writeText, 1}},
- {1870, {wxTextCtrl, xYToPosition, 2}},
- {1873, {wxNotebook, new_0, 0}},
- {1874, {wxNotebook, new_3, 3}},
- {1875, {wxNotebook, destruct, 0}},
- {1876, {wxNotebook, addPage, 3}},
- {1877, {wxNotebook, advanceSelection, 1}},
- {1878, {wxNotebook, assignImageList, 1}},
- {1879, {wxNotebook, create, 3}},
- {1880, {wxNotebook, deleteAllPages, 0}},
- {1881, {wxNotebook, deletePage, 1}},
- {1882, {wxNotebook, removePage, 1}},
- {1883, {wxNotebook, getCurrentPage, 0}},
- {1884, {wxNotebook, getImageList, 0}},
- {1886, {wxNotebook, getPage, 1}},
- {1887, {wxNotebook, getPageCount, 0}},
- {1888, {wxNotebook, getPageImage, 1}},
- {1889, {wxNotebook, getPageText, 1}},
- {1890, {wxNotebook, getRowCount, 0}},
- {1891, {wxNotebook, getSelection, 0}},
- {1892, {wxNotebook, getThemeBackgroundColour, 0}},
- {1894, {wxNotebook, hitTest, 2}},
- {1896, {wxNotebook, insertPage, 4}},
- {1897, {wxNotebook, setImageList, 1}},
- {1898, {wxNotebook, setPadding, 1}},
- {1899, {wxNotebook, setPageSize, 1}},
- {1900, {wxNotebook, setPageImage, 2}},
- {1901, {wxNotebook, setPageText, 2}},
- {1902, {wxNotebook, setSelection, 1}},
- {1903, {wxNotebook, changeSelection, 1}},
- {1904, {wxChoicebook, new_0, 0}},
- {1905, {wxChoicebook, new_3, 3}},
- {1906, {wxChoicebook, addPage, 3}},
- {1907, {wxChoicebook, advanceSelection, 1}},
- {1908, {wxChoicebook, assignImageList, 1}},
- {1909, {wxChoicebook, create, 3}},
- {1910, {wxChoicebook, deleteAllPages, 0}},
- {1911, {wxChoicebook, deletePage, 1}},
- {1912, {wxChoicebook, removePage, 1}},
- {1913, {wxChoicebook, getCurrentPage, 0}},
- {1914, {wxChoicebook, getImageList, 0}},
- {1916, {wxChoicebook, getPage, 1}},
- {1917, {wxChoicebook, getPageCount, 0}},
- {1918, {wxChoicebook, getPageImage, 1}},
- {1919, {wxChoicebook, getPageText, 1}},
- {1920, {wxChoicebook, getSelection, 0}},
- {1921, {wxChoicebook, hitTest, 2}},
- {1922, {wxChoicebook, insertPage, 4}},
- {1923, {wxChoicebook, setImageList, 1}},
- {1924, {wxChoicebook, setPageSize, 1}},
- {1925, {wxChoicebook, setPageImage, 2}},
- {1926, {wxChoicebook, setPageText, 2}},
- {1927, {wxChoicebook, setSelection, 1}},
- {1928, {wxChoicebook, changeSelection, 1}},
- {1929, {wxChoicebook, 'Destroy', undefined}},
- {1930, {wxToolbook, new_0, 0}},
- {1931, {wxToolbook, new_3, 3}},
- {1932, {wxToolbook, addPage, 3}},
- {1933, {wxToolbook, advanceSelection, 1}},
- {1934, {wxToolbook, assignImageList, 1}},
- {1935, {wxToolbook, create, 3}},
- {1936, {wxToolbook, deleteAllPages, 0}},
- {1937, {wxToolbook, deletePage, 1}},
- {1938, {wxToolbook, removePage, 1}},
- {1939, {wxToolbook, getCurrentPage, 0}},
- {1940, {wxToolbook, getImageList, 0}},
- {1942, {wxToolbook, getPage, 1}},
- {1943, {wxToolbook, getPageCount, 0}},
- {1944, {wxToolbook, getPageImage, 1}},
- {1945, {wxToolbook, getPageText, 1}},
- {1946, {wxToolbook, getSelection, 0}},
- {1948, {wxToolbook, hitTest, 2}},
- {1949, {wxToolbook, insertPage, 4}},
- {1950, {wxToolbook, setImageList, 1}},
- {1951, {wxToolbook, setPageSize, 1}},
- {1952, {wxToolbook, setPageImage, 2}},
- {1953, {wxToolbook, setPageText, 2}},
- {1954, {wxToolbook, setSelection, 1}},
- {1955, {wxToolbook, changeSelection, 1}},
- {1956, {wxToolbook, 'Destroy', undefined}},
- {1957, {wxListbook, new_0, 0}},
- {1958, {wxListbook, new_3, 3}},
- {1959, {wxListbook, addPage, 3}},
- {1960, {wxListbook, advanceSelection, 1}},
- {1961, {wxListbook, assignImageList, 1}},
- {1962, {wxListbook, create, 3}},
- {1963, {wxListbook, deleteAllPages, 0}},
- {1964, {wxListbook, deletePage, 1}},
- {1965, {wxListbook, removePage, 1}},
- {1966, {wxListbook, getCurrentPage, 0}},
- {1967, {wxListbook, getImageList, 0}},
- {1969, {wxListbook, getPage, 1}},
- {1970, {wxListbook, getPageCount, 0}},
- {1971, {wxListbook, getPageImage, 1}},
- {1972, {wxListbook, getPageText, 1}},
- {1973, {wxListbook, getSelection, 0}},
- {1975, {wxListbook, hitTest, 2}},
- {1976, {wxListbook, insertPage, 4}},
- {1977, {wxListbook, setImageList, 1}},
- {1978, {wxListbook, setPageSize, 1}},
- {1979, {wxListbook, setPageImage, 2}},
- {1980, {wxListbook, setPageText, 2}},
- {1981, {wxListbook, setSelection, 1}},
- {1982, {wxListbook, changeSelection, 1}},
- {1983, {wxListbook, 'Destroy', undefined}},
- {1984, {wxTreebook, new_0, 0}},
- {1985, {wxTreebook, new_3, 3}},
- {1986, {wxTreebook, addPage, 3}},
- {1987, {wxTreebook, advanceSelection, 1}},
- {1988, {wxTreebook, assignImageList, 1}},
- {1989, {wxTreebook, create, 3}},
- {1990, {wxTreebook, deleteAllPages, 0}},
- {1991, {wxTreebook, deletePage, 1}},
- {1992, {wxTreebook, removePage, 1}},
- {1993, {wxTreebook, getCurrentPage, 0}},
- {1994, {wxTreebook, getImageList, 0}},
- {1996, {wxTreebook, getPage, 1}},
- {1997, {wxTreebook, getPageCount, 0}},
- {1998, {wxTreebook, getPageImage, 1}},
- {1999, {wxTreebook, getPageText, 1}},
- {2000, {wxTreebook, getSelection, 0}},
- {2001, {wxTreebook, expandNode, 2}},
- {2002, {wxTreebook, isNodeExpanded, 1}},
- {2004, {wxTreebook, hitTest, 2}},
- {2005, {wxTreebook, insertPage, 4}},
- {2006, {wxTreebook, insertSubPage, 4}},
- {2007, {wxTreebook, setImageList, 1}},
- {2008, {wxTreebook, setPageSize, 1}},
- {2009, {wxTreebook, setPageImage, 2}},
- {2010, {wxTreebook, setPageText, 2}},
- {2011, {wxTreebook, setSelection, 1}},
- {2012, {wxTreebook, changeSelection, 1}},
- {2013, {wxTreebook, 'Destroy', undefined}},
- {2016, {wxTreeCtrl, new_2, 2}},
- {2017, {wxTreeCtrl, new_0, 0}},
- {2019, {wxTreeCtrl, destruct, 0}},
- {2020, {wxTreeCtrl, addRoot, 2}},
- {2021, {wxTreeCtrl, appendItem, 3}},
- {2022, {wxTreeCtrl, assignImageList, 1}},
- {2023, {wxTreeCtrl, assignStateImageList, 1}},
- {2024, {wxTreeCtrl, collapse, 1}},
- {2025, {wxTreeCtrl, collapseAndReset, 1}},
- {2026, {wxTreeCtrl, create, 2}},
- {2027, {wxTreeCtrl, delete, 1}},
- {2028, {wxTreeCtrl, deleteAllItems, 0}},
- {2029, {wxTreeCtrl, deleteChildren, 1}},
- {2030, {wxTreeCtrl, editLabel, 1}},
- {2031, {wxTreeCtrl, ensureVisible, 1}},
- {2032, {wxTreeCtrl, expand, 1}},
- {2033, {wxTreeCtrl, getBoundingRect, 3}},
- {2035, {wxTreeCtrl, getChildrenCount, 2}},
- {2036, {wxTreeCtrl, getCount, 0}},
- {2037, {wxTreeCtrl, getEditControl, 0}},
- {2038, {wxTreeCtrl, getFirstChild, 2}},
- {2039, {wxTreeCtrl, getNextChild, 2}},
- {2040, {wxTreeCtrl, getFirstVisibleItem, 0}},
- {2041, {wxTreeCtrl, getImageList, 0}},
- {2042, {wxTreeCtrl, getIndent, 0}},
- {2043, {wxTreeCtrl, getItemBackgroundColour, 1}},
- {2044, {wxTreeCtrl, getItemData, 1}},
- {2045, {wxTreeCtrl, getItemFont, 1}},
- {2046, {wxTreeCtrl, getItemImage_1, 1}},
- {2047, {wxTreeCtrl, getItemImage_2, 2}},
- {2048, {wxTreeCtrl, getItemText, 1}},
- {2049, {wxTreeCtrl, getItemTextColour, 1}},
- {2050, {wxTreeCtrl, getLastChild, 1}},
- {2051, {wxTreeCtrl, getNextSibling, 1}},
- {2052, {wxTreeCtrl, getNextVisible, 1}},
- {2053, {wxTreeCtrl, getItemParent, 1}},
- {2054, {wxTreeCtrl, getPrevSibling, 1}},
- {2055, {wxTreeCtrl, getPrevVisible, 1}},
- {2056, {wxTreeCtrl, getRootItem, 0}},
- {2057, {wxTreeCtrl, getSelection, 0}},
- {2058, {wxTreeCtrl, getSelections, 1}},
- {2059, {wxTreeCtrl, getStateImageList, 0}},
- {2060, {wxTreeCtrl, hitTest, 2}},
- {2062, {wxTreeCtrl, insertItem, 4}},
- {2063, {wxTreeCtrl, isBold, 1}},
- {2064, {wxTreeCtrl, isExpanded, 1}},
- {2065, {wxTreeCtrl, isSelected, 1}},
- {2066, {wxTreeCtrl, isVisible, 1}},
- {2067, {wxTreeCtrl, itemHasChildren, 1}},
- {2068, {wxTreeCtrl, isTreeItemIdOk, 1}},
- {2069, {wxTreeCtrl, prependItem, 3}},
- {2070, {wxTreeCtrl, scrollTo, 1}},
- {2071, {wxTreeCtrl, selectItem_1, 1}},
- {2072, {wxTreeCtrl, selectItem_2, 2}},
- {2073, {wxTreeCtrl, setIndent, 1}},
- {2074, {wxTreeCtrl, setImageList, 1}},
- {2075, {wxTreeCtrl, setItemBackgroundColour, 2}},
- {2076, {wxTreeCtrl, setItemBold, 2}},
- {2077, {wxTreeCtrl, setItemData, 2}},
- {2078, {wxTreeCtrl, setItemDropHighlight, 2}},
- {2079, {wxTreeCtrl, setItemFont, 2}},
- {2080, {wxTreeCtrl, setItemHasChildren, 2}},
- {2081, {wxTreeCtrl, setItemImage_2, 2}},
- {2082, {wxTreeCtrl, setItemImage_3, 3}},
- {2083, {wxTreeCtrl, setItemText, 2}},
- {2084, {wxTreeCtrl, setItemTextColour, 2}},
- {2085, {wxTreeCtrl, setStateImageList, 1}},
- {2086, {wxTreeCtrl, setWindowStyle, 1}},
- {2087, {wxTreeCtrl, sortChildren, 1}},
- {2088, {wxTreeCtrl, toggle, 1}},
- {2089, {wxTreeCtrl, toggleItemSelection, 1}},
- {2090, {wxTreeCtrl, unselect, 0}},
- {2091, {wxTreeCtrl, unselectAll, 0}},
- {2092, {wxTreeCtrl, unselectItem, 1}},
- {2093, {wxScrollBar, new_0, 0}},
- {2094, {wxScrollBar, new_3, 3}},
- {2095, {wxScrollBar, destruct, 0}},
- {2096, {wxScrollBar, create, 3}},
- {2097, {wxScrollBar, getRange, 0}},
- {2098, {wxScrollBar, getPageSize, 0}},
- {2099, {wxScrollBar, getThumbPosition, 0}},
- {2100, {wxScrollBar, getThumbSize, 0}},
- {2101, {wxScrollBar, setThumbPosition, 1}},
- {2102, {wxScrollBar, setScrollbar, 5}},
- {2104, {wxSpinButton, new_2, 2}},
- {2105, {wxSpinButton, new_0, 0}},
- {2106, {wxSpinButton, create, 2}},
- {2107, {wxSpinButton, getMax, 0}},
- {2108, {wxSpinButton, getMin, 0}},
- {2109, {wxSpinButton, getValue, 0}},
- {2110, {wxSpinButton, setRange, 2}},
- {2111, {wxSpinButton, setValue, 1}},
- {2112, {wxSpinButton, 'Destroy', undefined}},
- {2113, {wxSpinCtrl, new_0, 0}},
- {2114, {wxSpinCtrl, new_2, 2}},
- {2116, {wxSpinCtrl, create, 2}},
- {2119, {wxSpinCtrl, setValue_1_1, 1}},
- {2120, {wxSpinCtrl, setValue_1_0, 1}},
- {2122, {wxSpinCtrl, getValue, 0}},
- {2124, {wxSpinCtrl, setRange, 2}},
- {2125, {wxSpinCtrl, setSelection, 2}},
- {2127, {wxSpinCtrl, getMin, 0}},
- {2129, {wxSpinCtrl, getMax, 0}},
- {2130, {wxSpinCtrl, 'Destroy', undefined}},
- {2131, {wxStaticText, new_0, 0}},
- {2132, {wxStaticText, new_4, 4}},
- {2133, {wxStaticText, create, 4}},
- {2134, {wxStaticText, getLabel, 0}},
- {2135, {wxStaticText, setLabel, 1}},
- {2136, {wxStaticText, wrap, 1}},
- {2137, {wxStaticText, 'Destroy', undefined}},
- {2138, {wxStaticBitmap, new_0, 0}},
- {2139, {wxStaticBitmap, new_4, 4}},
- {2140, {wxStaticBitmap, create, 4}},
- {2141, {wxStaticBitmap, getBitmap, 0}},
- {2142, {wxStaticBitmap, setBitmap, 1}},
- {2143, {wxStaticBitmap, 'Destroy', undefined}},
- {2144, {wxRadioBox, new, 7}},
- {2146, {wxRadioBox, destruct, 0}},
- {2147, {wxRadioBox, create, 7}},
- {2148, {wxRadioBox, enable_2, 2}},
- {2149, {wxRadioBox, enable_1, 1}},
- {2150, {wxRadioBox, getSelection, 0}},
- {2151, {wxRadioBox, getString, 1}},
- {2152, {wxRadioBox, setSelection, 1}},
- {2153, {wxRadioBox, show_2, 2}},
- {2154, {wxRadioBox, show_1, 1}},
- {2155, {wxRadioBox, getColumnCount, 0}},
- {2156, {wxRadioBox, getItemHelpText, 1}},
- {2157, {wxRadioBox, getItemToolTip, 1}},
- {2159, {wxRadioBox, getItemFromPoint, 1}},
- {2160, {wxRadioBox, getRowCount, 0}},
- {2161, {wxRadioBox, isItemEnabled, 1}},
- {2162, {wxRadioBox, isItemShown, 1}},
- {2163, {wxRadioBox, setItemHelpText, 2}},
- {2164, {wxRadioBox, setItemToolTip, 2}},
- {2165, {wxRadioButton, new_0, 0}},
- {2166, {wxRadioButton, new_4, 4}},
- {2167, {wxRadioButton, create, 4}},
- {2168, {wxRadioButton, getValue, 0}},
- {2169, {wxRadioButton, setValue, 1}},
- {2170, {wxRadioButton, 'Destroy', undefined}},
- {2172, {wxSlider, new_6, 6}},
- {2173, {wxSlider, new_0, 0}},
- {2174, {wxSlider, create, 6}},
- {2175, {wxSlider, getLineSize, 0}},
- {2176, {wxSlider, getMax, 0}},
- {2177, {wxSlider, getMin, 0}},
- {2178, {wxSlider, getPageSize, 0}},
- {2179, {wxSlider, getThumbLength, 0}},
- {2180, {wxSlider, getValue, 0}},
- {2181, {wxSlider, setLineSize, 1}},
- {2182, {wxSlider, setPageSize, 1}},
- {2183, {wxSlider, setRange, 2}},
- {2184, {wxSlider, setThumbLength, 1}},
- {2185, {wxSlider, setValue, 1}},
- {2186, {wxSlider, 'Destroy', undefined}},
- {2188, {wxDialog, new_4, 4}},
- {2189, {wxDialog, new_0, 0}},
- {2191, {wxDialog, destruct, 0}},
- {2192, {wxDialog, create, 4}},
- {2193, {wxDialog, createButtonSizer, 1}},
- {2194, {wxDialog, createStdDialogButtonSizer, 1}},
- {2195, {wxDialog, endModal, 1}},
- {2196, {wxDialog, getAffirmativeId, 0}},
- {2197, {wxDialog, getReturnCode, 0}},
- {2198, {wxDialog, isModal, 0}},
- {2199, {wxDialog, setAffirmativeId, 1}},
- {2200, {wxDialog, setReturnCode, 1}},
- {2201, {wxDialog, show, 1}},
- {2202, {wxDialog, showModal, 0}},
- {2203, {wxColourDialog, new_0, 0}},
- {2204, {wxColourDialog, new_2, 2}},
- {2205, {wxColourDialog, destruct, 0}},
- {2206, {wxColourDialog, create, 2}},
- {2207, {wxColourDialog, getColourData, 0}},
- {2208, {wxColourData, new_0, 0}},
- {2209, {wxColourData, new_1, 1}},
- {2210, {wxColourData, destruct, 0}},
- {2211, {wxColourData, getChooseFull, 0}},
- {2212, {wxColourData, getColour, 0}},
- {2214, {wxColourData, getCustomColour, 1}},
- {2215, {wxColourData, setChooseFull, 1}},
- {2216, {wxColourData, setColour, 1}},
- {2217, {wxColourData, setCustomColour, 2}},
- {2218, {wxPalette, new_0, 0}},
- {2219, {wxPalette, new_4, 4}},
- {2221, {wxPalette, destruct, 0}},
- {2222, {wxPalette, create, 4}},
- {2223, {wxPalette, getColoursCount, 0}},
- {2224, {wxPalette, getPixel, 3}},
- {2225, {wxPalette, getRGB, 4}},
- {2226, {wxPalette, isOk, 0}},
- {2230, {wxDirDialog, new, 2}},
- {2231, {wxDirDialog, destruct, 0}},
- {2232, {wxDirDialog, getPath, 0}},
- {2233, {wxDirDialog, getMessage, 0}},
- {2234, {wxDirDialog, setMessage, 1}},
- {2235, {wxDirDialog, setPath, 1}},
- {2239, {wxFileDialog, new, 2}},
- {2240, {wxFileDialog, destruct, 0}},
- {2241, {wxFileDialog, getDirectory, 0}},
- {2242, {wxFileDialog, getFilename, 0}},
- {2243, {wxFileDialog, getFilenames, 1}},
- {2244, {wxFileDialog, getFilterIndex, 0}},
- {2245, {wxFileDialog, getMessage, 0}},
- {2246, {wxFileDialog, getPath, 0}},
- {2247, {wxFileDialog, getPaths, 1}},
- {2248, {wxFileDialog, getWildcard, 0}},
- {2249, {wxFileDialog, setDirectory, 1}},
- {2250, {wxFileDialog, setFilename, 1}},
- {2251, {wxFileDialog, setFilterIndex, 1}},
- {2252, {wxFileDialog, setMessage, 1}},
- {2253, {wxFileDialog, setPath, 1}},
- {2254, {wxFileDialog, setWildcard, 1}},
- {2255, {wxPickerBase, setInternalMargin, 1}},
- {2256, {wxPickerBase, getInternalMargin, 0}},
- {2257, {wxPickerBase, setTextCtrlProportion, 1}},
- {2258, {wxPickerBase, setPickerCtrlProportion, 1}},
- {2259, {wxPickerBase, getTextCtrlProportion, 0}},
- {2260, {wxPickerBase, getPickerCtrlProportion, 0}},
- {2261, {wxPickerBase, hasTextCtrl, 0}},
- {2262, {wxPickerBase, getTextCtrl, 0}},
- {2263, {wxPickerBase, isTextCtrlGrowable, 0}},
- {2264, {wxPickerBase, setPickerCtrlGrowable, 1}},
- {2265, {wxPickerBase, setTextCtrlGrowable, 1}},
- {2266, {wxPickerBase, isPickerCtrlGrowable, 0}},
- {2267, {wxFilePickerCtrl, new_0, 0}},
- {2268, {wxFilePickerCtrl, new_3, 3}},
- {2269, {wxFilePickerCtrl, create, 3}},
- {2270, {wxFilePickerCtrl, getPath, 0}},
- {2271, {wxFilePickerCtrl, setPath, 1}},
- {2272, {wxFilePickerCtrl, 'Destroy', undefined}},
- {2273, {wxDirPickerCtrl, new_0, 0}},
- {2274, {wxDirPickerCtrl, new_3, 3}},
- {2275, {wxDirPickerCtrl, create, 3}},
- {2276, {wxDirPickerCtrl, getPath, 0}},
- {2277, {wxDirPickerCtrl, setPath, 1}},
- {2278, {wxDirPickerCtrl, 'Destroy', undefined}},
- {2279, {wxColourPickerCtrl, new_0, 0}},
- {2280, {wxColourPickerCtrl, new_3, 3}},
- {2281, {wxColourPickerCtrl, create, 3}},
- {2282, {wxColourPickerCtrl, getColour, 0}},
- {2283, {wxColourPickerCtrl, setColour_1_1, 1}},
- {2284, {wxColourPickerCtrl, setColour_1_0, 1}},
- {2285, {wxColourPickerCtrl, 'Destroy', undefined}},
- {2286, {wxDatePickerCtrl, new_0, 0}},
- {2287, {wxDatePickerCtrl, new_3, 3}},
- {2288, {wxDatePickerCtrl, getRange, 2}},
- {2289, {wxDatePickerCtrl, getValue, 0}},
- {2290, {wxDatePickerCtrl, setRange, 2}},
- {2291, {wxDatePickerCtrl, setValue, 1}},
- {2292, {wxDatePickerCtrl, 'Destroy', undefined}},
- {2293, {wxFontPickerCtrl, new_0, 0}},
- {2294, {wxFontPickerCtrl, new_3, 3}},
- {2295, {wxFontPickerCtrl, create, 3}},
- {2296, {wxFontPickerCtrl, getSelectedFont, 0}},
- {2297, {wxFontPickerCtrl, setSelectedFont, 1}},
- {2298, {wxFontPickerCtrl, getMaxPointSize, 0}},
- {2299, {wxFontPickerCtrl, setMaxPointSize, 1}},
- {2300, {wxFontPickerCtrl, 'Destroy', undefined}},
- {2303, {wxFindReplaceDialog, new_0, 0}},
- {2304, {wxFindReplaceDialog, new_4, 4}},
- {2305, {wxFindReplaceDialog, destruct, 0}},
- {2306, {wxFindReplaceDialog, create, 4}},
- {2307, {wxFindReplaceDialog, getData, 0}},
- {2308, {wxFindReplaceData, new_0, 0}},
- {2309, {wxFindReplaceData, new_1, 1}},
- {2310, {wxFindReplaceData, getFindString, 0}},
- {2311, {wxFindReplaceData, getReplaceString, 0}},
- {2312, {wxFindReplaceData, getFlags, 0}},
- {2313, {wxFindReplaceData, setFlags, 1}},
- {2314, {wxFindReplaceData, setFindString, 1}},
- {2315, {wxFindReplaceData, setReplaceString, 1}},
- {2316, {wxFindReplaceData, 'Destroy', undefined}},
- {2317, {wxMultiChoiceDialog, new_0, 0}},
- {2319, {wxMultiChoiceDialog, new_5, 5}},
- {2320, {wxMultiChoiceDialog, getSelections, 0}},
- {2321, {wxMultiChoiceDialog, setSelections, 1}},
- {2322, {wxMultiChoiceDialog, 'Destroy', undefined}},
- {2323, {wxSingleChoiceDialog, new_0, 0}},
- {2325, {wxSingleChoiceDialog, new_5, 5}},
- {2326, {wxSingleChoiceDialog, getSelection, 0}},
- {2327, {wxSingleChoiceDialog, getStringSelection, 0}},
- {2328, {wxSingleChoiceDialog, setSelection, 1}},
- {2329, {wxSingleChoiceDialog, 'Destroy', undefined}},
- {2330, {wxTextEntryDialog, new, 3}},
- {2331, {wxTextEntryDialog, getValue, 0}},
- {2332, {wxTextEntryDialog, setValue, 1}},
- {2333, {wxTextEntryDialog, 'Destroy', undefined}},
- {2334, {wxPasswordEntryDialog, new, 3}},
- {2335, {wxPasswordEntryDialog, 'Destroy', undefined}},
- {2336, {wxFontData, new_0, 0}},
- {2337, {wxFontData, new_1, 1}},
- {2338, {wxFontData, destruct, 0}},
- {2339, {wxFontData, enableEffects, 1}},
- {2340, {wxFontData, getAllowSymbols, 0}},
- {2341, {wxFontData, getColour, 0}},
- {2342, {wxFontData, getChosenFont, 0}},
- {2343, {wxFontData, getEnableEffects, 0}},
- {2344, {wxFontData, getInitialFont, 0}},
- {2345, {wxFontData, getShowHelp, 0}},
- {2346, {wxFontData, setAllowSymbols, 1}},
- {2347, {wxFontData, setChosenFont, 1}},
- {2348, {wxFontData, setColour, 1}},
- {2349, {wxFontData, setInitialFont, 1}},
- {2350, {wxFontData, setRange, 2}},
- {2351, {wxFontData, setShowHelp, 1}},
- {2355, {wxFontDialog, new_0, 0}},
- {2357, {wxFontDialog, new_2, 2}},
- {2359, {wxFontDialog, create, 2}},
- {2360, {wxFontDialog, getFontData, 0}},
- {2362, {wxFontDialog, 'Destroy', undefined}},
- {2363, {wxProgressDialog, new, 3}},
- {2364, {wxProgressDialog, destruct, 0}},
- {2365, {wxProgressDialog, resume, 0}},
- {2366, {wxProgressDialog, update_2, 2}},
- {2367, {wxProgressDialog, update_0, 0}},
- {2368, {wxMessageDialog, new, 3}},
- {2369, {wxMessageDialog, destruct, 0}},
- {2370, {wxPageSetupDialog, new, 2}},
- {2371, {wxPageSetupDialog, destruct, 0}},
- {2372, {wxPageSetupDialog, getPageSetupData, 0}},
- {2373, {wxPageSetupDialog, showModal, 0}},
- {2374, {wxPageSetupDialogData, new_0, 0}},
- {2375, {wxPageSetupDialogData, new_1_0, 1}},
- {2376, {wxPageSetupDialogData, new_1_1, 1}},
- {2377, {wxPageSetupDialogData, destruct, 0}},
- {2378, {wxPageSetupDialogData, enableHelp, 1}},
- {2379, {wxPageSetupDialogData, enableMargins, 1}},
- {2380, {wxPageSetupDialogData, enableOrientation, 1}},
- {2381, {wxPageSetupDialogData, enablePaper, 1}},
- {2382, {wxPageSetupDialogData, enablePrinter, 1}},
- {2383, {wxPageSetupDialogData, getDefaultMinMargins, 0}},
- {2384, {wxPageSetupDialogData, getEnableMargins, 0}},
- {2385, {wxPageSetupDialogData, getEnableOrientation, 0}},
- {2386, {wxPageSetupDialogData, getEnablePaper, 0}},
- {2387, {wxPageSetupDialogData, getEnablePrinter, 0}},
- {2388, {wxPageSetupDialogData, getEnableHelp, 0}},
- {2389, {wxPageSetupDialogData, getDefaultInfo, 0}},
- {2390, {wxPageSetupDialogData, getMarginTopLeft, 0}},
- {2391, {wxPageSetupDialogData, getMarginBottomRight, 0}},
- {2392, {wxPageSetupDialogData, getMinMarginTopLeft, 0}},
- {2393, {wxPageSetupDialogData, getMinMarginBottomRight, 0}},
- {2394, {wxPageSetupDialogData, getPaperId, 0}},
- {2395, {wxPageSetupDialogData, getPaperSize, 0}},
- {2397, {wxPageSetupDialogData, getPrintData, 0}},
- {2398, {wxPageSetupDialogData, isOk, 0}},
- {2399, {wxPageSetupDialogData, setDefaultInfo, 1}},
- {2400, {wxPageSetupDialogData, setDefaultMinMargins, 1}},
- {2401, {wxPageSetupDialogData, setMarginTopLeft, 1}},
- {2402, {wxPageSetupDialogData, setMarginBottomRight, 1}},
- {2403, {wxPageSetupDialogData, setMinMarginTopLeft, 1}},
- {2404, {wxPageSetupDialogData, setMinMarginBottomRight, 1}},
- {2405, {wxPageSetupDialogData, setPaperId, 1}},
- {2406, {wxPageSetupDialogData, setPaperSize_1_1, 1}},
- {2407, {wxPageSetupDialogData, setPaperSize_1_0, 1}},
- {2408, {wxPageSetupDialogData, setPrintData, 1}},
- {2409, {wxPrintDialog, new_2_0, 2}},
- {2410, {wxPrintDialog, new_2_1, 2}},
- {2411, {wxPrintDialog, destruct, 0}},
- {2412, {wxPrintDialog, getPrintDialogData, 0}},
- {2413, {wxPrintDialog, getPrintDC, 0}},
- {2414, {wxPrintDialogData, new_0, 0}},
- {2415, {wxPrintDialogData, new_1_1, 1}},
- {2416, {wxPrintDialogData, new_1_0, 1}},
- {2417, {wxPrintDialogData, destruct, 0}},
- {2418, {wxPrintDialogData, enableHelp, 1}},
- {2419, {wxPrintDialogData, enablePageNumbers, 1}},
- {2420, {wxPrintDialogData, enablePrintToFile, 1}},
- {2421, {wxPrintDialogData, enableSelection, 1}},
- {2422, {wxPrintDialogData, getAllPages, 0}},
- {2423, {wxPrintDialogData, getCollate, 0}},
- {2424, {wxPrintDialogData, getFromPage, 0}},
- {2425, {wxPrintDialogData, getMaxPage, 0}},
- {2426, {wxPrintDialogData, getMinPage, 0}},
- {2427, {wxPrintDialogData, getNoCopies, 0}},
- {2428, {wxPrintDialogData, getPrintData, 0}},
- {2429, {wxPrintDialogData, getPrintToFile, 0}},
- {2430, {wxPrintDialogData, getSelection, 0}},
- {2431, {wxPrintDialogData, getToPage, 0}},
- {2432, {wxPrintDialogData, isOk, 0}},
- {2433, {wxPrintDialogData, setCollate, 1}},
- {2434, {wxPrintDialogData, setFromPage, 1}},
- {2435, {wxPrintDialogData, setMaxPage, 1}},
- {2436, {wxPrintDialogData, setMinPage, 1}},
- {2437, {wxPrintDialogData, setNoCopies, 1}},
- {2438, {wxPrintDialogData, setPrintData, 1}},
- {2439, {wxPrintDialogData, setPrintToFile, 1}},
- {2440, {wxPrintDialogData, setSelection, 1}},
- {2441, {wxPrintDialogData, setToPage, 1}},
- {2442, {wxPrintData, new_0, 0}},
- {2443, {wxPrintData, new_1, 1}},
- {2444, {wxPrintData, destruct, 0}},
- {2445, {wxPrintData, getCollate, 0}},
- {2446, {wxPrintData, getBin, 0}},
- {2447, {wxPrintData, getColour, 0}},
- {2448, {wxPrintData, getDuplex, 0}},
- {2449, {wxPrintData, getNoCopies, 0}},
- {2450, {wxPrintData, getOrientation, 0}},
- {2451, {wxPrintData, getPaperId, 0}},
- {2452, {wxPrintData, getPrinterName, 0}},
- {2453, {wxPrintData, getQuality, 0}},
- {2454, {wxPrintData, isOk, 0}},
- {2455, {wxPrintData, setBin, 1}},
- {2456, {wxPrintData, setCollate, 1}},
- {2457, {wxPrintData, setColour, 1}},
- {2458, {wxPrintData, setDuplex, 1}},
- {2459, {wxPrintData, setNoCopies, 1}},
- {2460, {wxPrintData, setOrientation, 1}},
- {2461, {wxPrintData, setPaperId, 1}},
- {2462, {wxPrintData, setPrinterName, 1}},
- {2463, {wxPrintData, setQuality, 1}},
- {2466, {wxPrintPreview, new_2, 2}},
- {2467, {wxPrintPreview, new_3, 3}},
- {2469, {wxPrintPreview, destruct, 0}},
- {2470, {wxPrintPreview, getCanvas, 0}},
- {2471, {wxPrintPreview, getCurrentPage, 0}},
- {2472, {wxPrintPreview, getFrame, 0}},
- {2473, {wxPrintPreview, getMaxPage, 0}},
- {2474, {wxPrintPreview, getMinPage, 0}},
- {2475, {wxPrintPreview, getPrintout, 0}},
- {2476, {wxPrintPreview, getPrintoutForPrinting, 0}},
- {2477, {wxPrintPreview, isOk, 0}},
- {2478, {wxPrintPreview, paintPage, 2}},
- {2479, {wxPrintPreview, print, 1}},
- {2480, {wxPrintPreview, renderPage, 1}},
- {2481, {wxPrintPreview, setCanvas, 1}},
- {2482, {wxPrintPreview, setCurrentPage, 1}},
- {2483, {wxPrintPreview, setFrame, 1}},
- {2484, {wxPrintPreview, setPrintout, 1}},
- {2485, {wxPrintPreview, setZoom, 1}},
- {2486, {wxPreviewFrame, new, 3}},
- {2487, {wxPreviewFrame, destruct, 0}},
- {2488, {wxPreviewFrame, createControlBar, 0}},
- {2489, {wxPreviewFrame, createCanvas, 0}},
- {2490, {wxPreviewFrame, initialize, 0}},
- {2491, {wxPreviewFrame, onCloseWindow, 1}},
- {2492, {wxPreviewControlBar, new, 4}},
- {2493, {wxPreviewControlBar, destruct, 0}},
- {2494, {wxPreviewControlBar, createButtons, 0}},
- {2495, {wxPreviewControlBar, getPrintPreview, 0}},
- {2496, {wxPreviewControlBar, getZoomControl, 0}},
- {2497, {wxPreviewControlBar, setZoomControl, 1}},
- {2499, {wxPrinter, new, 1}},
- {2500, {wxPrinter, createAbortWindow, 2}},
- {2501, {wxPrinter, getAbort, 0}},
- {2502, {wxPrinter, getLastError, 0}},
- {2503, {wxPrinter, getPrintDialogData, 0}},
- {2504, {wxPrinter, print, 3}},
- {2505, {wxPrinter, printDialog, 1}},
- {2506, {wxPrinter, reportError, 3}},
- {2507, {wxPrinter, setup, 1}},
- {2508, {wxPrinter, 'Destroy', undefined}},
- {2509, {wxXmlResource, new_1, 1}},
- {2510, {wxXmlResource, new_2, 2}},
- {2511, {wxXmlResource, destruct, 0}},
- {2512, {wxXmlResource, attachUnknownControl, 3}},
- {2513, {wxXmlResource, clearHandlers, 0}},
- {2514, {wxXmlResource, compareVersion, 4}},
- {2515, {wxXmlResource, get, 0}},
- {2516, {wxXmlResource, getFlags, 0}},
- {2517, {wxXmlResource, getVersion, 0}},
- {2518, {wxXmlResource, getXRCID, 2}},
- {2519, {wxXmlResource, initAllHandlers, 0}},
- {2520, {wxXmlResource, load, 1}},
- {2521, {wxXmlResource, loadBitmap, 1}},
- {2522, {wxXmlResource, loadDialog_2, 2}},
- {2523, {wxXmlResource, loadDialog_3, 3}},
- {2524, {wxXmlResource, loadFrame_2, 2}},
- {2525, {wxXmlResource, loadFrame_3, 3}},
- {2526, {wxXmlResource, loadIcon, 1}},
- {2527, {wxXmlResource, loadMenu, 1}},
- {2528, {wxXmlResource, loadMenuBar_2, 2}},
- {2529, {wxXmlResource, loadMenuBar_1, 1}},
- {2530, {wxXmlResource, loadPanel_2, 2}},
- {2531, {wxXmlResource, loadPanel_3, 3}},
- {2532, {wxXmlResource, loadToolBar, 2}},
- {2533, {wxXmlResource, set, 1}},
- {2534, {wxXmlResource, setFlags, 1}},
- {2535, {wxXmlResource, unload, 1}},
- {2536, {wxXmlResource, xrcctrl, 3}},
- {2537, {wxHtmlEasyPrinting, new, 1}},
- {2538, {wxHtmlEasyPrinting, destruct, 0}},
- {2539, {wxHtmlEasyPrinting, getPrintData, 0}},
- {2540, {wxHtmlEasyPrinting, getPageSetupData, 0}},
- {2541, {wxHtmlEasyPrinting, previewFile, 1}},
- {2542, {wxHtmlEasyPrinting, previewText, 2}},
- {2543, {wxHtmlEasyPrinting, printFile, 1}},
- {2544, {wxHtmlEasyPrinting, printText, 2}},
- {2545, {wxHtmlEasyPrinting, pageSetup, 0}},
- {2546, {wxHtmlEasyPrinting, setFonts, 3}},
- {2547, {wxHtmlEasyPrinting, setHeader, 2}},
- {2548, {wxHtmlEasyPrinting, setFooter, 2}},
- {2550, {wxGLCanvas, new_2, 2}},
- {2551, {wxGLCanvas, new_3_1, 3}},
- {2552, {wxGLCanvas, new_3_0, 3}},
- {2553, {wxGLCanvas, getContext, 0}},
- {2555, {wxGLCanvas, setCurrent, 0}},
- {2556, {wxGLCanvas, swapBuffers, 0}},
- {2557, {wxGLCanvas, 'Destroy', undefined}},
- {2558, {wxAuiManager, new, 1}},
- {2559, {wxAuiManager, destruct, 0}},
- {2560, {wxAuiManager, addPane_2_1, 2}},
- {2561, {wxAuiManager, addPane_3, 3}},
- {2562, {wxAuiManager, addPane_2_0, 2}},
- {2563, {wxAuiManager, detachPane, 1}},
- {2564, {wxAuiManager, getAllPanes, 0}},
- {2565, {wxAuiManager, getArtProvider, 0}},
- {2566, {wxAuiManager, getDockSizeConstraint, 2}},
- {2567, {wxAuiManager, getFlags, 0}},
- {2568, {wxAuiManager, getManagedWindow, 0}},
- {2569, {wxAuiManager, getManager, 1}},
- {2570, {wxAuiManager, getPane_1_1, 1}},
- {2571, {wxAuiManager, getPane_1_0, 1}},
- {2572, {wxAuiManager, hideHint, 0}},
- {2573, {wxAuiManager, insertPane, 3}},
- {2574, {wxAuiManager, loadPaneInfo, 2}},
- {2575, {wxAuiManager, loadPerspective, 2}},
- {2576, {wxAuiManager, savePaneInfo, 1}},
- {2577, {wxAuiManager, savePerspective, 0}},
- {2578, {wxAuiManager, setArtProvider, 1}},
- {2579, {wxAuiManager, setDockSizeConstraint, 2}},
- {2580, {wxAuiManager, setFlags, 1}},
- {2581, {wxAuiManager, setManagedWindow, 1}},
- {2582, {wxAuiManager, showHint, 1}},
- {2583, {wxAuiManager, unInit, 0}},
- {2584, {wxAuiManager, update, 0}},
- {2585, {wxAuiPaneInfo, new_0, 0}},
- {2586, {wxAuiPaneInfo, new_1, 1}},
- {2587, {wxAuiPaneInfo, destruct, 0}},
- {2588, {wxAuiPaneInfo, bestSize_1, 1}},
- {2589, {wxAuiPaneInfo, bestSize_2, 2}},
- {2590, {wxAuiPaneInfo, bottom, 0}},
- {2591, {wxAuiPaneInfo, bottomDockable, 1}},
- {2592, {wxAuiPaneInfo, caption, 1}},
- {2593, {wxAuiPaneInfo, captionVisible, 1}},
- {2594, {wxAuiPaneInfo, centre, 0}},
- {2595, {wxAuiPaneInfo, centrePane, 0}},
- {2596, {wxAuiPaneInfo, closeButton, 1}},
- {2597, {wxAuiPaneInfo, defaultPane, 0}},
- {2598, {wxAuiPaneInfo, destroyOnClose, 1}},
- {2599, {wxAuiPaneInfo, direction, 1}},
- {2600, {wxAuiPaneInfo, dock, 0}},
- {2601, {wxAuiPaneInfo, dockable, 1}},
- {2602, {wxAuiPaneInfo, fixed, 0}},
- {2603, {wxAuiPaneInfo, float, 0}},
- {2604, {wxAuiPaneInfo, floatable, 1}},
- {2605, {wxAuiPaneInfo, floatingPosition_1, 1}},
- {2606, {wxAuiPaneInfo, floatingPosition_2, 2}},
- {2607, {wxAuiPaneInfo, floatingSize_1, 1}},
- {2608, {wxAuiPaneInfo, floatingSize_2, 2}},
- {2609, {wxAuiPaneInfo, gripper, 1}},
- {2610, {wxAuiPaneInfo, gripperTop, 1}},
- {2611, {wxAuiPaneInfo, hasBorder, 0}},
- {2612, {wxAuiPaneInfo, hasCaption, 0}},
- {2613, {wxAuiPaneInfo, hasCloseButton, 0}},
- {2614, {wxAuiPaneInfo, hasFlag, 1}},
- {2615, {wxAuiPaneInfo, hasGripper, 0}},
- {2616, {wxAuiPaneInfo, hasGripperTop, 0}},
- {2617, {wxAuiPaneInfo, hasMaximizeButton, 0}},
- {2618, {wxAuiPaneInfo, hasMinimizeButton, 0}},
- {2619, {wxAuiPaneInfo, hasPinButton, 0}},
- {2620, {wxAuiPaneInfo, hide, 0}},
- {2621, {wxAuiPaneInfo, isBottomDockable, 0}},
- {2622, {wxAuiPaneInfo, isDocked, 0}},
- {2623, {wxAuiPaneInfo, isFixed, 0}},
- {2624, {wxAuiPaneInfo, isFloatable, 0}},
- {2625, {wxAuiPaneInfo, isFloating, 0}},
- {2626, {wxAuiPaneInfo, isLeftDockable, 0}},
- {2627, {wxAuiPaneInfo, isMovable, 0}},
- {2628, {wxAuiPaneInfo, isOk, 0}},
- {2629, {wxAuiPaneInfo, isResizable, 0}},
- {2630, {wxAuiPaneInfo, isRightDockable, 0}},
- {2631, {wxAuiPaneInfo, isShown, 0}},
- {2632, {wxAuiPaneInfo, isToolbar, 0}},
- {2633, {wxAuiPaneInfo, isTopDockable, 0}},
- {2634, {wxAuiPaneInfo, layer, 1}},
- {2635, {wxAuiPaneInfo, left, 0}},
- {2636, {wxAuiPaneInfo, leftDockable, 1}},
- {2637, {wxAuiPaneInfo, maxSize_1, 1}},
- {2638, {wxAuiPaneInfo, maxSize_2, 2}},
- {2639, {wxAuiPaneInfo, maximizeButton, 1}},
- {2640, {wxAuiPaneInfo, minSize_1, 1}},
- {2641, {wxAuiPaneInfo, minSize_2, 2}},
- {2642, {wxAuiPaneInfo, minimizeButton, 1}},
- {2643, {wxAuiPaneInfo, movable, 1}},
- {2644, {wxAuiPaneInfo, name, 1}},
- {2645, {wxAuiPaneInfo, paneBorder, 1}},
- {2646, {wxAuiPaneInfo, pinButton, 1}},
- {2647, {wxAuiPaneInfo, position, 1}},
- {2648, {wxAuiPaneInfo, resizable, 1}},
- {2649, {wxAuiPaneInfo, right, 0}},
- {2650, {wxAuiPaneInfo, rightDockable, 1}},
- {2651, {wxAuiPaneInfo, row, 1}},
- {2652, {wxAuiPaneInfo, safeSet, 1}},
- {2653, {wxAuiPaneInfo, setFlag, 2}},
- {2654, {wxAuiPaneInfo, show, 1}},
- {2655, {wxAuiPaneInfo, toolbarPane, 0}},
- {2656, {wxAuiPaneInfo, top, 0}},
- {2657, {wxAuiPaneInfo, topDockable, 1}},
- {2658, {wxAuiPaneInfo, window, 1}},
- {2659, {wxAuiPaneInfo, getWindow, 0}},
- {2660, {wxAuiPaneInfo, getFrame, 0}},
- {2661, {wxAuiPaneInfo, getDirection, 0}},
- {2662, {wxAuiPaneInfo, getLayer, 0}},
- {2663, {wxAuiPaneInfo, getRow, 0}},
- {2664, {wxAuiPaneInfo, getPosition, 0}},
- {2665, {wxAuiPaneInfo, getFloatingPosition, 0}},
- {2666, {wxAuiPaneInfo, getFloatingSize, 0}},
- {2667, {wxAuiNotebook, new_0, 0}},
- {2668, {wxAuiNotebook, new_2, 2}},
- {2669, {wxAuiNotebook, addPage, 3}},
- {2670, {wxAuiNotebook, create, 2}},
- {2671, {wxAuiNotebook, deletePage, 1}},
- {2672, {wxAuiNotebook, getArtProvider, 0}},
- {2673, {wxAuiNotebook, getPage, 1}},
- {2674, {wxAuiNotebook, getPageBitmap, 1}},
- {2675, {wxAuiNotebook, getPageCount, 0}},
- {2676, {wxAuiNotebook, getPageIndex, 1}},
- {2677, {wxAuiNotebook, getPageText, 1}},
- {2678, {wxAuiNotebook, getSelection, 0}},
- {2679, {wxAuiNotebook, insertPage, 4}},
- {2680, {wxAuiNotebook, removePage, 1}},
- {2681, {wxAuiNotebook, setArtProvider, 1}},
- {2682, {wxAuiNotebook, setFont, 1}},
- {2683, {wxAuiNotebook, setPageBitmap, 2}},
- {2684, {wxAuiNotebook, setPageText, 2}},
- {2685, {wxAuiNotebook, setSelection, 1}},
- {2686, {wxAuiNotebook, setTabCtrlHeight, 1}},
- {2687, {wxAuiNotebook, setUniformBitmapSize, 1}},
- {2688, {wxAuiNotebook, 'Destroy', undefined}},
- {2689, {wxAuiTabArt, setFlags, 1}},
- {2690, {wxAuiTabArt, setMeasuringFont, 1}},
- {2691, {wxAuiTabArt, setNormalFont, 1}},
- {2692, {wxAuiTabArt, setSelectedFont, 1}},
- {2693, {wxAuiTabArt, setColour, 1}},
- {2694, {wxAuiTabArt, setActiveColour, 1}},
- {2695, {wxAuiDockArt, getColour, 1}},
- {2696, {wxAuiDockArt, getFont, 1}},
- {2697, {wxAuiDockArt, getMetric, 1}},
- {2698, {wxAuiDockArt, setColour, 2}},
- {2699, {wxAuiDockArt, setFont, 2}},
- {2700, {wxAuiDockArt, setMetric, 2}},
- {2701, {wxAuiSimpleTabArt, new, 0}},
- {2702, {wxAuiSimpleTabArt, 'Destroy', undefined}},
- {2703, {wxMDIParentFrame, new_0, 0}},
- {2704, {wxMDIParentFrame, new_4, 4}},
- {2705, {wxMDIParentFrame, destruct, 0}},
- {2706, {wxMDIParentFrame, activateNext, 0}},
- {2707, {wxMDIParentFrame, activatePrevious, 0}},
- {2708, {wxMDIParentFrame, arrangeIcons, 0}},
- {2709, {wxMDIParentFrame, cascade, 0}},
- {2710, {wxMDIParentFrame, create, 4}},
- {2711, {wxMDIParentFrame, getActiveChild, 0}},
- {2712, {wxMDIParentFrame, getClientWindow, 0}},
- {2713, {wxMDIParentFrame, tile, 1}},
- {2714, {wxMDIChildFrame, new_0, 0}},
- {2715, {wxMDIChildFrame, new_4, 4}},
- {2716, {wxMDIChildFrame, destruct, 0}},
- {2717, {wxMDIChildFrame, activate, 0}},
- {2718, {wxMDIChildFrame, create, 4}},
- {2719, {wxMDIChildFrame, maximize, 1}},
- {2720, {wxMDIChildFrame, restore, 0}},
- {2721, {wxMDIClientWindow, new_0, 0}},
- {2722, {wxMDIClientWindow, new_2, 2}},
- {2723, {wxMDIClientWindow, destruct, 0}},
- {2724, {wxMDIClientWindow, createClient, 2}},
- {2725, {wxLayoutAlgorithm, new, 0}},
- {2726, {wxLayoutAlgorithm, layoutFrame, 2}},
- {2727, {wxLayoutAlgorithm, layoutMDIFrame, 2}},
- {2728, {wxLayoutAlgorithm, layoutWindow, 2}},
- {2729, {wxLayoutAlgorithm, 'Destroy', undefined}},
- {2730, {wxEvent, getId, 0}},
- {2731, {wxEvent, getSkipped, 0}},
- {2732, {wxEvent, getTimestamp, 0}},
- {2733, {wxEvent, isCommandEvent, 0}},
- {2734, {wxEvent, resumePropagation, 1}},
- {2735, {wxEvent, shouldPropagate, 0}},
- {2736, {wxEvent, skip, 1}},
- {2737, {wxEvent, stopPropagation, 0}},
- {2738, {wxCommandEvent, getClientData, 0}},
- {2739, {wxCommandEvent, getExtraLong, 0}},
- {2740, {wxCommandEvent, getInt, 0}},
- {2741, {wxCommandEvent, getSelection, 0}},
- {2742, {wxCommandEvent, getString, 0}},
- {2743, {wxCommandEvent, isChecked, 0}},
- {2744, {wxCommandEvent, isSelection, 0}},
- {2745, {wxCommandEvent, setInt, 1}},
- {2746, {wxCommandEvent, setString, 1}},
- {2747, {wxScrollEvent, getOrientation, 0}},
- {2748, {wxScrollEvent, getPosition, 0}},
- {2749, {wxScrollWinEvent, getOrientation, 0}},
- {2750, {wxScrollWinEvent, getPosition, 0}},
- {2751, {wxMouseEvent, altDown, 0}},
- {2752, {wxMouseEvent, button, 1}},
- {2753, {wxMouseEvent, buttonDClick, 1}},
- {2754, {wxMouseEvent, buttonDown, 1}},
- {2755, {wxMouseEvent, buttonUp, 1}},
- {2756, {wxMouseEvent, cmdDown, 0}},
- {2757, {wxMouseEvent, controlDown, 0}},
- {2758, {wxMouseEvent, dragging, 0}},
- {2759, {wxMouseEvent, entering, 0}},
- {2760, {wxMouseEvent, getButton, 0}},
- {2763, {wxMouseEvent, getPosition, 0}},
- {2764, {wxMouseEvent, getLogicalPosition, 1}},
- {2765, {wxMouseEvent, getLinesPerAction, 0}},
- {2766, {wxMouseEvent, getWheelRotation, 0}},
- {2767, {wxMouseEvent, getWheelDelta, 0}},
- {2768, {wxMouseEvent, getX, 0}},
- {2769, {wxMouseEvent, getY, 0}},
- {2770, {wxMouseEvent, isButton, 0}},
- {2771, {wxMouseEvent, isPageScroll, 0}},
- {2772, {wxMouseEvent, leaving, 0}},
- {2773, {wxMouseEvent, leftDClick, 0}},
- {2774, {wxMouseEvent, leftDown, 0}},
- {2775, {wxMouseEvent, leftIsDown, 0}},
- {2776, {wxMouseEvent, leftUp, 0}},
- {2777, {wxMouseEvent, metaDown, 0}},
- {2778, {wxMouseEvent, middleDClick, 0}},
- {2779, {wxMouseEvent, middleDown, 0}},
- {2780, {wxMouseEvent, middleIsDown, 0}},
- {2781, {wxMouseEvent, middleUp, 0}},
- {2782, {wxMouseEvent, moving, 0}},
- {2783, {wxMouseEvent, rightDClick, 0}},
- {2784, {wxMouseEvent, rightDown, 0}},
- {2785, {wxMouseEvent, rightIsDown, 0}},
- {2786, {wxMouseEvent, rightUp, 0}},
- {2787, {wxMouseEvent, shiftDown, 0}},
- {2788, {wxSetCursorEvent, getCursor, 0}},
- {2789, {wxSetCursorEvent, getX, 0}},
- {2790, {wxSetCursorEvent, getY, 0}},
- {2791, {wxSetCursorEvent, hasCursor, 0}},
- {2792, {wxSetCursorEvent, setCursor, 1}},
- {2793, {wxKeyEvent, altDown, 0}},
- {2794, {wxKeyEvent, cmdDown, 0}},
- {2795, {wxKeyEvent, controlDown, 0}},
- {2796, {wxKeyEvent, getKeyCode, 0}},
- {2797, {wxKeyEvent, getModifiers, 0}},
- {2800, {wxKeyEvent, getPosition, 0}},
- {2801, {wxKeyEvent, getRawKeyCode, 0}},
- {2802, {wxKeyEvent, getRawKeyFlags, 0}},
- {2803, {wxKeyEvent, getUnicodeKey, 0}},
- {2804, {wxKeyEvent, getX, 0}},
- {2805, {wxKeyEvent, getY, 0}},
- {2806, {wxKeyEvent, hasModifiers, 0}},
- {2807, {wxKeyEvent, metaDown, 0}},
- {2808, {wxKeyEvent, shiftDown, 0}},
- {2809, {wxSizeEvent, getSize, 0}},
- {2810, {wxMoveEvent, getPosition, 0}},
- {2811, {wxEraseEvent, getDC, 0}},
- {2812, {wxFocusEvent, getWindow, 0}},
- {2813, {wxChildFocusEvent, getWindow, 0}},
- {2814, {wxMenuEvent, getMenu, 0}},
- {2815, {wxMenuEvent, getMenuId, 0}},
- {2816, {wxMenuEvent, isPopup, 0}},
- {2817, {wxCloseEvent, canVeto, 0}},
- {2818, {wxCloseEvent, getLoggingOff, 0}},
- {2819, {wxCloseEvent, setCanVeto, 1}},
- {2820, {wxCloseEvent, setLoggingOff, 1}},
- {2821, {wxCloseEvent, veto, 1}},
- {2822, {wxShowEvent, setShow, 1}},
- {2823, {wxShowEvent, getShow, 0}},
- {2824, {wxIconizeEvent, iconized, 0}},
- {2825, {wxJoystickEvent, buttonDown, 1}},
- {2826, {wxJoystickEvent, buttonIsDown, 1}},
- {2827, {wxJoystickEvent, buttonUp, 1}},
- {2828, {wxJoystickEvent, getButtonChange, 0}},
- {2829, {wxJoystickEvent, getButtonState, 0}},
- {2830, {wxJoystickEvent, getJoystick, 0}},
- {2831, {wxJoystickEvent, getPosition, 0}},
- {2832, {wxJoystickEvent, getZPosition, 0}},
- {2833, {wxJoystickEvent, isButton, 0}},
- {2834, {wxJoystickEvent, isMove, 0}},
- {2835, {wxJoystickEvent, isZMove, 0}},
- {2836, {wxUpdateUIEvent, canUpdate, 1}},
- {2837, {wxUpdateUIEvent, check, 1}},
- {2838, {wxUpdateUIEvent, enable, 1}},
- {2839, {wxUpdateUIEvent, show, 1}},
- {2840, {wxUpdateUIEvent, getChecked, 0}},
- {2841, {wxUpdateUIEvent, getEnabled, 0}},
- {2842, {wxUpdateUIEvent, getShown, 0}},
- {2843, {wxUpdateUIEvent, getSetChecked, 0}},
- {2844, {wxUpdateUIEvent, getSetEnabled, 0}},
- {2845, {wxUpdateUIEvent, getSetShown, 0}},
- {2846, {wxUpdateUIEvent, getSetText, 0}},
- {2847, {wxUpdateUIEvent, getText, 0}},
- {2848, {wxUpdateUIEvent, getMode, 0}},
- {2849, {wxUpdateUIEvent, getUpdateInterval, 0}},
- {2850, {wxUpdateUIEvent, resetUpdateTime, 0}},
- {2851, {wxUpdateUIEvent, setMode, 1}},
- {2852, {wxUpdateUIEvent, setText, 1}},
- {2853, {wxUpdateUIEvent, setUpdateInterval, 1}},
- {2854, {wxMouseCaptureChangedEvent, getCapturedWindow, 0}},
- {2855, {wxPaletteChangedEvent, setChangedWindow, 1}},
- {2856, {wxPaletteChangedEvent, getChangedWindow, 0}},
- {2857, {wxQueryNewPaletteEvent, setPaletteRealized, 1}},
- {2858, {wxQueryNewPaletteEvent, getPaletteRealized, 0}},
- {2859, {wxNavigationKeyEvent, getDirection, 0}},
- {2860, {wxNavigationKeyEvent, setDirection, 1}},
- {2861, {wxNavigationKeyEvent, isWindowChange, 0}},
- {2862, {wxNavigationKeyEvent, setWindowChange, 1}},
- {2863, {wxNavigationKeyEvent, isFromTab, 0}},
- {2864, {wxNavigationKeyEvent, setFromTab, 1}},
- {2865, {wxNavigationKeyEvent, getCurrentFocus, 0}},
- {2866, {wxNavigationKeyEvent, setCurrentFocus, 1}},
- {2867, {wxHelpEvent, getOrigin, 0}},
- {2868, {wxHelpEvent, getPosition, 0}},
- {2869, {wxHelpEvent, setOrigin, 1}},
- {2870, {wxHelpEvent, setPosition, 1}},
- {2871, {wxContextMenuEvent, getPosition, 0}},
- {2872, {wxContextMenuEvent, setPosition, 1}},
- {2873, {wxIdleEvent, canSend, 1}},
- {2874, {wxIdleEvent, getMode, 0}},
- {2875, {wxIdleEvent, requestMore, 1}},
- {2876, {wxIdleEvent, moreRequested, 0}},
- {2877, {wxIdleEvent, setMode, 1}},
- {2878, {wxGridEvent, altDown, 0}},
- {2879, {wxGridEvent, controlDown, 0}},
- {2880, {wxGridEvent, getCol, 0}},
- {2881, {wxGridEvent, getPosition, 0}},
- {2882, {wxGridEvent, getRow, 0}},
- {2883, {wxGridEvent, metaDown, 0}},
- {2884, {wxGridEvent, selecting, 0}},
- {2885, {wxGridEvent, shiftDown, 0}},
- {2886, {wxNotifyEvent, allow, 0}},
- {2887, {wxNotifyEvent, isAllowed, 0}},
- {2888, {wxNotifyEvent, veto, 0}},
- {2889, {wxSashEvent, getEdge, 0}},
- {2890, {wxSashEvent, getDragRect, 0}},
- {2891, {wxSashEvent, getDragStatus, 0}},
- {2892, {wxListEvent, getCacheFrom, 0}},
- {2893, {wxListEvent, getCacheTo, 0}},
- {2894, {wxListEvent, getKeyCode, 0}},
- {2895, {wxListEvent, getIndex, 0}},
- {2896, {wxListEvent, getColumn, 0}},
- {2897, {wxListEvent, getPoint, 0}},
- {2898, {wxListEvent, getLabel, 0}},
- {2899, {wxListEvent, getText, 0}},
- {2900, {wxListEvent, getImage, 0}},
- {2901, {wxListEvent, getData, 0}},
- {2902, {wxListEvent, getMask, 0}},
- {2903, {wxListEvent, getItem, 0}},
- {2904, {wxListEvent, isEditCancelled, 0}},
- {2905, {wxDateEvent, getDate, 0}},
- {2906, {wxCalendarEvent, getWeekDay, 0}},
- {2907, {wxFileDirPickerEvent, getPath, 0}},
- {2908, {wxColourPickerEvent, getColour, 0}},
- {2909, {wxFontPickerEvent, getFont, 0}},
- {2910, {wxStyledTextEvent, getPosition, 0}},
- {2911, {wxStyledTextEvent, getKey, 0}},
- {2912, {wxStyledTextEvent, getModifiers, 0}},
- {2913, {wxStyledTextEvent, getModificationType, 0}},
- {2914, {wxStyledTextEvent, getText, 0}},
- {2915, {wxStyledTextEvent, getLength, 0}},
- {2916, {wxStyledTextEvent, getLinesAdded, 0}},
- {2917, {wxStyledTextEvent, getLine, 0}},
- {2918, {wxStyledTextEvent, getFoldLevelNow, 0}},
- {2919, {wxStyledTextEvent, getFoldLevelPrev, 0}},
- {2920, {wxStyledTextEvent, getMargin, 0}},
- {2921, {wxStyledTextEvent, getMessage, 0}},
- {2922, {wxStyledTextEvent, getWParam, 0}},
- {2923, {wxStyledTextEvent, getLParam, 0}},
- {2924, {wxStyledTextEvent, getListType, 0}},
- {2925, {wxStyledTextEvent, getX, 0}},
- {2926, {wxStyledTextEvent, getY, 0}},
- {2927, {wxStyledTextEvent, getDragText, 0}},
- {2928, {wxStyledTextEvent, getDragAllowMove, 0}},
- {2929, {wxStyledTextEvent, getDragResult, 0}},
- {2930, {wxStyledTextEvent, getShift, 0}},
- {2931, {wxStyledTextEvent, getControl, 0}},
- {2932, {wxStyledTextEvent, getAlt, 0}},
- {2933, {utils, getKeyState, 1}},
- {2934, {utils, getMousePosition, 2}},
- {2935, {utils, getMouseState, 0}},
- {2936, {utils, setDetectableAutoRepeat, 1}},
- {2937, {utils, bell, 0}},
- {2938, {utils, findMenuItemId, 3}},
- {2939, {utils, genericFindWindowAtPoint, 1}},
- {2940, {utils, findWindowAtPoint, 1}},
- {2941, {utils, beginBusyCursor, 1}},
- {2942, {utils, endBusyCursor, 0}},
- {2943, {utils, isBusy, 0}},
- {2944, {utils, shutdown, 1}},
- {2945, {utils, shell, 1}},
- {2946, {utils, launchDefaultBrowser, 2}},
- {2947, {utils, getEmailAddress, 0}},
- {2948, {utils, getUserId, 0}},
- {2949, {utils, getHomeDir, 0}},
- {2950, {utils, newId, 0}},
- {2951, {utils, registerId, 1}},
- {2952, {utils, getCurrentId, 0}},
- {2953, {utils, getOsDescription, 0}},
- {2954, {utils, isPlatformLittleEndian, 0}},
- {2955, {utils, isPlatform64Bit, 0}},
- {2956, {gdicmn, displaySize, 2}},
- {2957, {gdicmn, setCursor, 1}},
- {2958, {wxPrintout, new, 1}},
- {2959, {wxPrintout, destruct, 0}},
- {2960, {wxPrintout, getDC, 0}},
- {2961, {wxPrintout, getPageSizeMM, 2}},
- {2962, {wxPrintout, getPageSizePixels, 2}},
- {2963, {wxPrintout, getPaperRectPixels, 0}},
- {2964, {wxPrintout, getPPIPrinter, 2}},
- {2965, {wxPrintout, getPPIScreen, 2}},
- {2966, {wxPrintout, getTitle, 0}},
- {2967, {wxPrintout, isPreview, 0}},
- {2968, {wxPrintout, fitThisSizeToPaper, 1}},
- {2969, {wxPrintout, fitThisSizeToPage, 1}},
- {2970, {wxPrintout, fitThisSizeToPageMargins, 2}},
- {2971, {wxPrintout, mapScreenSizeToPaper, 0}},
- {2972, {wxPrintout, mapScreenSizeToPage, 0}},
- {2973, {wxPrintout, mapScreenSizeToPageMargins, 1}},
- {2974, {wxPrintout, mapScreenSizeToDevice, 0}},
- {2975, {wxPrintout, getLogicalPaperRect, 0}},
- {2976, {wxPrintout, getLogicalPageRect, 0}},
- {2977, {wxPrintout, getLogicalPageMarginsRect, 1}},
- {2978, {wxPrintout, setLogicalOrigin, 2}},
- {2979, {wxPrintout, offsetLogicalOrigin, 2}},
- {2980, {wxStyledTextCtrl, new_2, 2}},
- {2981, {wxStyledTextCtrl, new_0, 0}},
- {2982, {wxStyledTextCtrl, destruct, 0}},
- {2983, {wxStyledTextCtrl, create, 2}},
- {2984, {wxStyledTextCtrl, addText, 1}},
- {2985, {wxStyledTextCtrl, addStyledText, 1}},
- {2986, {wxStyledTextCtrl, insertText, 2}},
- {2987, {wxStyledTextCtrl, clearAll, 0}},
- {2988, {wxStyledTextCtrl, clearDocumentStyle, 0}},
- {2989, {wxStyledTextCtrl, getLength, 0}},
- {2990, {wxStyledTextCtrl, getCharAt, 1}},
- {2991, {wxStyledTextCtrl, getCurrentPos, 0}},
- {2992, {wxStyledTextCtrl, getAnchor, 0}},
- {2993, {wxStyledTextCtrl, getStyleAt, 1}},
- {2994, {wxStyledTextCtrl, redo, 0}},
- {2995, {wxStyledTextCtrl, setUndoCollection, 1}},
- {2996, {wxStyledTextCtrl, selectAll, 0}},
- {2997, {wxStyledTextCtrl, setSavePoint, 0}},
- {2998, {wxStyledTextCtrl, getStyledText, 2}},
- {2999, {wxStyledTextCtrl, canRedo, 0}},
- {3000, {wxStyledTextCtrl, markerLineFromHandle, 1}},
- {3001, {wxStyledTextCtrl, markerDeleteHandle, 1}},
- {3002, {wxStyledTextCtrl, getUndoCollection, 0}},
- {3003, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
- {3004, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
- {3005, {wxStyledTextCtrl, positionFromPoint, 1}},
- {3006, {wxStyledTextCtrl, positionFromPointClose, 2}},
- {3007, {wxStyledTextCtrl, gotoLine, 1}},
- {3008, {wxStyledTextCtrl, gotoPos, 1}},
- {3009, {wxStyledTextCtrl, setAnchor, 1}},
- {3010, {wxStyledTextCtrl, getCurLine, 1}},
- {3011, {wxStyledTextCtrl, getEndStyled, 0}},
- {3012, {wxStyledTextCtrl, convertEOLs, 1}},
- {3013, {wxStyledTextCtrl, getEOLMode, 0}},
- {3014, {wxStyledTextCtrl, setEOLMode, 1}},
- {3015, {wxStyledTextCtrl, startStyling, 2}},
- {3016, {wxStyledTextCtrl, setStyling, 2}},
- {3017, {wxStyledTextCtrl, getBufferedDraw, 0}},
- {3018, {wxStyledTextCtrl, setBufferedDraw, 1}},
- {3019, {wxStyledTextCtrl, setTabWidth, 1}},
- {3020, {wxStyledTextCtrl, getTabWidth, 0}},
- {3021, {wxStyledTextCtrl, setCodePage, 1}},
- {3022, {wxStyledTextCtrl, markerDefine, 3}},
- {3023, {wxStyledTextCtrl, markerSetForeground, 2}},
- {3024, {wxStyledTextCtrl, markerSetBackground, 2}},
- {3025, {wxStyledTextCtrl, markerAdd, 2}},
- {3026, {wxStyledTextCtrl, markerDelete, 2}},
- {3027, {wxStyledTextCtrl, markerDeleteAll, 1}},
- {3028, {wxStyledTextCtrl, markerGet, 1}},
- {3029, {wxStyledTextCtrl, markerNext, 2}},
- {3030, {wxStyledTextCtrl, markerPrevious, 2}},
- {3031, {wxStyledTextCtrl, markerDefineBitmap, 2}},
- {3032, {wxStyledTextCtrl, markerAddSet, 2}},
- {3033, {wxStyledTextCtrl, markerSetAlpha, 2}},
- {3034, {wxStyledTextCtrl, setMarginType, 2}},
- {3035, {wxStyledTextCtrl, getMarginType, 1}},
- {3036, {wxStyledTextCtrl, setMarginWidth, 2}},
- {3037, {wxStyledTextCtrl, getMarginWidth, 1}},
- {3038, {wxStyledTextCtrl, setMarginMask, 2}},
- {3039, {wxStyledTextCtrl, getMarginMask, 1}},
- {3040, {wxStyledTextCtrl, setMarginSensitive, 2}},
- {3041, {wxStyledTextCtrl, getMarginSensitive, 1}},
- {3042, {wxStyledTextCtrl, styleClearAll, 0}},
- {3043, {wxStyledTextCtrl, styleSetForeground, 2}},
- {3044, {wxStyledTextCtrl, styleSetBackground, 2}},
- {3045, {wxStyledTextCtrl, styleSetBold, 2}},
- {3046, {wxStyledTextCtrl, styleSetItalic, 2}},
- {3047, {wxStyledTextCtrl, styleSetSize, 2}},
- {3048, {wxStyledTextCtrl, styleSetFaceName, 2}},
- {3049, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
- {3050, {wxStyledTextCtrl, styleResetDefault, 0}},
- {3051, {wxStyledTextCtrl, styleSetUnderline, 2}},
- {3052, {wxStyledTextCtrl, styleSetCase, 2}},
- {3053, {wxStyledTextCtrl, styleSetHotSpot, 2}},
- {3054, {wxStyledTextCtrl, setSelForeground, 2}},
- {3055, {wxStyledTextCtrl, setSelBackground, 2}},
- {3056, {wxStyledTextCtrl, getSelAlpha, 0}},
- {3057, {wxStyledTextCtrl, setSelAlpha, 1}},
- {3058, {wxStyledTextCtrl, setCaretForeground, 1}},
- {3059, {wxStyledTextCtrl, cmdKeyAssign, 3}},
- {3060, {wxStyledTextCtrl, cmdKeyClear, 2}},
- {3061, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
- {3062, {wxStyledTextCtrl, setStyleBytes, 2}},
- {3063, {wxStyledTextCtrl, styleSetVisible, 2}},
- {3064, {wxStyledTextCtrl, getCaretPeriod, 0}},
- {3065, {wxStyledTextCtrl, setCaretPeriod, 1}},
- {3066, {wxStyledTextCtrl, setWordChars, 1}},
- {3067, {wxStyledTextCtrl, beginUndoAction, 0}},
- {3068, {wxStyledTextCtrl, endUndoAction, 0}},
- {3069, {wxStyledTextCtrl, indicatorSetStyle, 2}},
- {3070, {wxStyledTextCtrl, indicatorGetStyle, 1}},
- {3071, {wxStyledTextCtrl, indicatorSetForeground, 2}},
- {3072, {wxStyledTextCtrl, indicatorGetForeground, 1}},
- {3073, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
- {3074, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
- {3075, {wxStyledTextCtrl, getStyleBits, 0}},
- {3076, {wxStyledTextCtrl, setLineState, 2}},
- {3077, {wxStyledTextCtrl, getLineState, 1}},
- {3078, {wxStyledTextCtrl, getMaxLineState, 0}},
- {3079, {wxStyledTextCtrl, getCaretLineVisible, 0}},
- {3080, {wxStyledTextCtrl, setCaretLineVisible, 1}},
- {3081, {wxStyledTextCtrl, getCaretLineBackground, 0}},
- {3082, {wxStyledTextCtrl, setCaretLineBackground, 1}},
- {3083, {wxStyledTextCtrl, autoCompShow, 2}},
- {3084, {wxStyledTextCtrl, autoCompCancel, 0}},
- {3085, {wxStyledTextCtrl, autoCompActive, 0}},
- {3086, {wxStyledTextCtrl, autoCompPosStart, 0}},
- {3087, {wxStyledTextCtrl, autoCompComplete, 0}},
- {3088, {wxStyledTextCtrl, autoCompStops, 1}},
- {3089, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
- {3090, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
- {3091, {wxStyledTextCtrl, autoCompSelect, 1}},
- {3092, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
- {3093, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
- {3094, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
- {3095, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
- {3096, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
- {3097, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
- {3098, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
- {3099, {wxStyledTextCtrl, userListShow, 2}},
- {3100, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
- {3101, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
- {3102, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
- {3103, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
- {3104, {wxStyledTextCtrl, registerImage, 2}},
- {3105, {wxStyledTextCtrl, clearRegisteredImages, 0}},
- {3106, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
- {3107, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
- {3108, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
- {3109, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
- {3110, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
- {3111, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
- {3112, {wxStyledTextCtrl, setIndent, 1}},
- {3113, {wxStyledTextCtrl, getIndent, 0}},
- {3114, {wxStyledTextCtrl, setUseTabs, 1}},
- {3115, {wxStyledTextCtrl, getUseTabs, 0}},
- {3116, {wxStyledTextCtrl, setLineIndentation, 2}},
- {3117, {wxStyledTextCtrl, getLineIndentation, 1}},
- {3118, {wxStyledTextCtrl, getLineIndentPosition, 1}},
- {3119, {wxStyledTextCtrl, getColumn, 1}},
- {3120, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
- {3121, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
- {3122, {wxStyledTextCtrl, setIndentationGuides, 1}},
- {3123, {wxStyledTextCtrl, getIndentationGuides, 0}},
- {3124, {wxStyledTextCtrl, setHighlightGuide, 1}},
- {3125, {wxStyledTextCtrl, getHighlightGuide, 0}},
- {3126, {wxStyledTextCtrl, getLineEndPosition, 1}},
- {3127, {wxStyledTextCtrl, getCodePage, 0}},
- {3128, {wxStyledTextCtrl, getCaretForeground, 0}},
- {3129, {wxStyledTextCtrl, getReadOnly, 0}},
- {3130, {wxStyledTextCtrl, setCurrentPos, 1}},
- {3131, {wxStyledTextCtrl, setSelectionStart, 1}},
- {3132, {wxStyledTextCtrl, getSelectionStart, 0}},
- {3133, {wxStyledTextCtrl, setSelectionEnd, 1}},
- {3134, {wxStyledTextCtrl, getSelectionEnd, 0}},
- {3135, {wxStyledTextCtrl, setPrintMagnification, 1}},
- {3136, {wxStyledTextCtrl, getPrintMagnification, 0}},
- {3137, {wxStyledTextCtrl, setPrintColourMode, 1}},
- {3138, {wxStyledTextCtrl, getPrintColourMode, 0}},
- {3139, {wxStyledTextCtrl, findText, 4}},
- {3140, {wxStyledTextCtrl, formatRange, 7}},
- {3141, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
- {3142, {wxStyledTextCtrl, getLine, 1}},
- {3143, {wxStyledTextCtrl, getLineCount, 0}},
- {3144, {wxStyledTextCtrl, setMarginLeft, 1}},
- {3145, {wxStyledTextCtrl, getMarginLeft, 0}},
- {3146, {wxStyledTextCtrl, setMarginRight, 1}},
- {3147, {wxStyledTextCtrl, getMarginRight, 0}},
- {3148, {wxStyledTextCtrl, getModify, 0}},
- {3149, {wxStyledTextCtrl, setSelection, 2}},
- {3150, {wxStyledTextCtrl, getSelectedText, 0}},
- {3151, {wxStyledTextCtrl, getTextRange, 2}},
- {3152, {wxStyledTextCtrl, hideSelection, 1}},
- {3153, {wxStyledTextCtrl, lineFromPosition, 1}},
- {3154, {wxStyledTextCtrl, positionFromLine, 1}},
- {3155, {wxStyledTextCtrl, lineScroll, 2}},
- {3156, {wxStyledTextCtrl, ensureCaretVisible, 0}},
- {3157, {wxStyledTextCtrl, replaceSelection, 1}},
- {3158, {wxStyledTextCtrl, setReadOnly, 1}},
- {3159, {wxStyledTextCtrl, canPaste, 0}},
- {3160, {wxStyledTextCtrl, canUndo, 0}},
- {3161, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
- {3162, {wxStyledTextCtrl, undo, 0}},
- {3163, {wxStyledTextCtrl, cut, 0}},
- {3164, {wxStyledTextCtrl, copy, 0}},
- {3165, {wxStyledTextCtrl, paste, 0}},
- {3166, {wxStyledTextCtrl, clear, 0}},
- {3167, {wxStyledTextCtrl, setText, 1}},
- {3168, {wxStyledTextCtrl, getText, 0}},
- {3169, {wxStyledTextCtrl, getTextLength, 0}},
- {3170, {wxStyledTextCtrl, getOvertype, 0}},
- {3171, {wxStyledTextCtrl, setCaretWidth, 1}},
- {3172, {wxStyledTextCtrl, getCaretWidth, 0}},
- {3173, {wxStyledTextCtrl, setTargetStart, 1}},
- {3174, {wxStyledTextCtrl, getTargetStart, 0}},
- {3175, {wxStyledTextCtrl, setTargetEnd, 1}},
- {3176, {wxStyledTextCtrl, getTargetEnd, 0}},
- {3177, {wxStyledTextCtrl, replaceTarget, 1}},
- {3178, {wxStyledTextCtrl, searchInTarget, 1}},
- {3179, {wxStyledTextCtrl, setSearchFlags, 1}},
- {3180, {wxStyledTextCtrl, getSearchFlags, 0}},
- {3181, {wxStyledTextCtrl, callTipShow, 2}},
- {3182, {wxStyledTextCtrl, callTipCancel, 0}},
- {3183, {wxStyledTextCtrl, callTipActive, 0}},
- {3184, {wxStyledTextCtrl, callTipPosAtStart, 0}},
- {3185, {wxStyledTextCtrl, callTipSetHighlight, 2}},
- {3186, {wxStyledTextCtrl, callTipSetBackground, 1}},
- {3187, {wxStyledTextCtrl, callTipSetForeground, 1}},
- {3188, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
- {3189, {wxStyledTextCtrl, callTipUseStyle, 1}},
- {3190, {wxStyledTextCtrl, visibleFromDocLine, 1}},
- {3191, {wxStyledTextCtrl, docLineFromVisible, 1}},
- {3192, {wxStyledTextCtrl, wrapCount, 1}},
- {3193, {wxStyledTextCtrl, setFoldLevel, 2}},
- {3194, {wxStyledTextCtrl, getFoldLevel, 1}},
- {3195, {wxStyledTextCtrl, getLastChild, 2}},
- {3196, {wxStyledTextCtrl, getFoldParent, 1}},
- {3197, {wxStyledTextCtrl, showLines, 2}},
- {3198, {wxStyledTextCtrl, hideLines, 2}},
- {3199, {wxStyledTextCtrl, getLineVisible, 1}},
- {3200, {wxStyledTextCtrl, setFoldExpanded, 2}},
- {3201, {wxStyledTextCtrl, getFoldExpanded, 1}},
- {3202, {wxStyledTextCtrl, toggleFold, 1}},
- {3203, {wxStyledTextCtrl, ensureVisible, 1}},
- {3204, {wxStyledTextCtrl, setFoldFlags, 1}},
- {3205, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
- {3206, {wxStyledTextCtrl, setTabIndents, 1}},
- {3207, {wxStyledTextCtrl, getTabIndents, 0}},
- {3208, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
- {3209, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
- {3210, {wxStyledTextCtrl, setMouseDwellTime, 1}},
- {3211, {wxStyledTextCtrl, getMouseDwellTime, 0}},
- {3212, {wxStyledTextCtrl, wordStartPosition, 2}},
- {3213, {wxStyledTextCtrl, wordEndPosition, 2}},
- {3214, {wxStyledTextCtrl, setWrapMode, 1}},
- {3215, {wxStyledTextCtrl, getWrapMode, 0}},
- {3216, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
- {3217, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
- {3218, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
- {3219, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
- {3220, {wxStyledTextCtrl, setWrapStartIndent, 1}},
- {3221, {wxStyledTextCtrl, getWrapStartIndent, 0}},
- {3222, {wxStyledTextCtrl, setLayoutCache, 1}},
- {3223, {wxStyledTextCtrl, getLayoutCache, 0}},
- {3224, {wxStyledTextCtrl, setScrollWidth, 1}},
- {3225, {wxStyledTextCtrl, getScrollWidth, 0}},
- {3226, {wxStyledTextCtrl, textWidth, 2}},
- {3227, {wxStyledTextCtrl, getEndAtLastLine, 0}},
- {3228, {wxStyledTextCtrl, textHeight, 1}},
- {3229, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
- {3230, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
- {3231, {wxStyledTextCtrl, appendText, 1}},
- {3232, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
- {3233, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
- {3234, {wxStyledTextCtrl, targetFromSelection, 0}},
- {3235, {wxStyledTextCtrl, linesJoin, 0}},
- {3236, {wxStyledTextCtrl, linesSplit, 1}},
- {3237, {wxStyledTextCtrl, setFoldMarginColour, 2}},
- {3238, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
- {3239, {wxStyledTextCtrl, lineDown, 0}},
- {3240, {wxStyledTextCtrl, lineDownExtend, 0}},
- {3241, {wxStyledTextCtrl, lineUp, 0}},
- {3242, {wxStyledTextCtrl, lineUpExtend, 0}},
- {3243, {wxStyledTextCtrl, charLeft, 0}},
- {3244, {wxStyledTextCtrl, charLeftExtend, 0}},
- {3245, {wxStyledTextCtrl, charRight, 0}},
- {3246, {wxStyledTextCtrl, charRightExtend, 0}},
- {3247, {wxStyledTextCtrl, wordLeft, 0}},
- {3248, {wxStyledTextCtrl, wordLeftExtend, 0}},
- {3249, {wxStyledTextCtrl, wordRight, 0}},
- {3250, {wxStyledTextCtrl, wordRightExtend, 0}},
- {3251, {wxStyledTextCtrl, home, 0}},
- {3252, {wxStyledTextCtrl, homeExtend, 0}},
- {3253, {wxStyledTextCtrl, lineEnd, 0}},
- {3254, {wxStyledTextCtrl, lineEndExtend, 0}},
- {3255, {wxStyledTextCtrl, documentStart, 0}},
- {3256, {wxStyledTextCtrl, documentStartExtend, 0}},
- {3257, {wxStyledTextCtrl, documentEnd, 0}},
- {3258, {wxStyledTextCtrl, documentEndExtend, 0}},
- {3259, {wxStyledTextCtrl, pageUp, 0}},
- {3260, {wxStyledTextCtrl, pageUpExtend, 0}},
- {3261, {wxStyledTextCtrl, pageDown, 0}},
- {3262, {wxStyledTextCtrl, pageDownExtend, 0}},
- {3263, {wxStyledTextCtrl, editToggleOvertype, 0}},
- {3264, {wxStyledTextCtrl, cancel, 0}},
- {3265, {wxStyledTextCtrl, deleteBack, 0}},
- {3266, {wxStyledTextCtrl, tab, 0}},
- {3267, {wxStyledTextCtrl, backTab, 0}},
- {3268, {wxStyledTextCtrl, newLine, 0}},
- {3269, {wxStyledTextCtrl, formFeed, 0}},
- {3270, {wxStyledTextCtrl, vCHome, 0}},
- {3271, {wxStyledTextCtrl, vCHomeExtend, 0}},
- {3272, {wxStyledTextCtrl, zoomIn, 0}},
- {3273, {wxStyledTextCtrl, zoomOut, 0}},
- {3274, {wxStyledTextCtrl, delWordLeft, 0}},
- {3275, {wxStyledTextCtrl, delWordRight, 0}},
- {3276, {wxStyledTextCtrl, lineCut, 0}},
- {3277, {wxStyledTextCtrl, lineDelete, 0}},
- {3278, {wxStyledTextCtrl, lineTranspose, 0}},
- {3279, {wxStyledTextCtrl, lineDuplicate, 0}},
- {3280, {wxStyledTextCtrl, lowerCase, 0}},
- {3281, {wxStyledTextCtrl, upperCase, 0}},
- {3282, {wxStyledTextCtrl, lineScrollDown, 0}},
- {3283, {wxStyledTextCtrl, lineScrollUp, 0}},
- {3284, {wxStyledTextCtrl, deleteBackNotLine, 0}},
- {3285, {wxStyledTextCtrl, homeDisplay, 0}},
- {3286, {wxStyledTextCtrl, homeDisplayExtend, 0}},
- {3287, {wxStyledTextCtrl, lineEndDisplay, 0}},
- {3288, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
- {3289, {wxStyledTextCtrl, homeWrapExtend, 0}},
- {3290, {wxStyledTextCtrl, lineEndWrap, 0}},
- {3291, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
- {3292, {wxStyledTextCtrl, vCHomeWrap, 0}},
- {3293, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
- {3294, {wxStyledTextCtrl, lineCopy, 0}},
- {3295, {wxStyledTextCtrl, moveCaretInsideView, 0}},
- {3296, {wxStyledTextCtrl, lineLength, 1}},
- {3297, {wxStyledTextCtrl, braceHighlight, 2}},
- {3298, {wxStyledTextCtrl, braceBadLight, 1}},
- {3299, {wxStyledTextCtrl, braceMatch, 1}},
- {3300, {wxStyledTextCtrl, getViewEOL, 0}},
- {3301, {wxStyledTextCtrl, setViewEOL, 1}},
- {3302, {wxStyledTextCtrl, setModEventMask, 1}},
- {3303, {wxStyledTextCtrl, getEdgeColumn, 0}},
- {3304, {wxStyledTextCtrl, setEdgeColumn, 1}},
- {3305, {wxStyledTextCtrl, setEdgeMode, 1}},
- {3306, {wxStyledTextCtrl, getEdgeMode, 0}},
- {3307, {wxStyledTextCtrl, getEdgeColour, 0}},
- {3308, {wxStyledTextCtrl, setEdgeColour, 1}},
- {3309, {wxStyledTextCtrl, searchAnchor, 0}},
- {3310, {wxStyledTextCtrl, searchNext, 2}},
- {3311, {wxStyledTextCtrl, searchPrev, 2}},
- {3312, {wxStyledTextCtrl, linesOnScreen, 0}},
- {3313, {wxStyledTextCtrl, usePopUp, 1}},
- {3314, {wxStyledTextCtrl, selectionIsRectangle, 0}},
- {3315, {wxStyledTextCtrl, setZoom, 1}},
- {3316, {wxStyledTextCtrl, getZoom, 0}},
- {3317, {wxStyledTextCtrl, getModEventMask, 0}},
- {3318, {wxStyledTextCtrl, setSTCFocus, 1}},
- {3319, {wxStyledTextCtrl, getSTCFocus, 0}},
- {3320, {wxStyledTextCtrl, setStatus, 1}},
- {3321, {wxStyledTextCtrl, getStatus, 0}},
- {3322, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
- {3323, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
- {3324, {wxStyledTextCtrl, setSTCCursor, 1}},
- {3325, {wxStyledTextCtrl, getSTCCursor, 0}},
- {3326, {wxStyledTextCtrl, setControlCharSymbol, 1}},
- {3327, {wxStyledTextCtrl, getControlCharSymbol, 0}},
- {3328, {wxStyledTextCtrl, wordPartLeft, 0}},
- {3329, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
- {3330, {wxStyledTextCtrl, wordPartRight, 0}},
- {3331, {wxStyledTextCtrl, wordPartRightExtend, 0}},
- {3332, {wxStyledTextCtrl, setVisiblePolicy, 2}},
- {3333, {wxStyledTextCtrl, delLineLeft, 0}},
- {3334, {wxStyledTextCtrl, delLineRight, 0}},
- {3335, {wxStyledTextCtrl, getXOffset, 0}},
- {3336, {wxStyledTextCtrl, chooseCaretX, 0}},
- {3337, {wxStyledTextCtrl, setXCaretPolicy, 2}},
- {3338, {wxStyledTextCtrl, setYCaretPolicy, 2}},
- {3339, {wxStyledTextCtrl, getPrintWrapMode, 0}},
- {3340, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
- {3341, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
- {3342, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
- {3343, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
- {3344, {wxStyledTextCtrl, paraDownExtend, 0}},
- {3345, {wxStyledTextCtrl, paraUp, 0}},
- {3346, {wxStyledTextCtrl, paraUpExtend, 0}},
- {3347, {wxStyledTextCtrl, positionBefore, 1}},
- {3348, {wxStyledTextCtrl, positionAfter, 1}},
- {3349, {wxStyledTextCtrl, copyRange, 2}},
- {3350, {wxStyledTextCtrl, copyText, 2}},
- {3351, {wxStyledTextCtrl, setSelectionMode, 1}},
- {3352, {wxStyledTextCtrl, getSelectionMode, 0}},
- {3353, {wxStyledTextCtrl, lineDownRectExtend, 0}},
- {3354, {wxStyledTextCtrl, lineUpRectExtend, 0}},
- {3355, {wxStyledTextCtrl, charLeftRectExtend, 0}},
- {3356, {wxStyledTextCtrl, charRightRectExtend, 0}},
- {3357, {wxStyledTextCtrl, homeRectExtend, 0}},
- {3358, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
- {3359, {wxStyledTextCtrl, lineEndRectExtend, 0}},
- {3360, {wxStyledTextCtrl, pageUpRectExtend, 0}},
- {3361, {wxStyledTextCtrl, pageDownRectExtend, 0}},
- {3362, {wxStyledTextCtrl, stutteredPageUp, 0}},
- {3363, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
- {3364, {wxStyledTextCtrl, stutteredPageDown, 0}},
- {3365, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
- {3366, {wxStyledTextCtrl, wordLeftEnd, 0}},
- {3367, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
- {3368, {wxStyledTextCtrl, wordRightEnd, 0}},
- {3369, {wxStyledTextCtrl, wordRightEndExtend, 0}},
- {3370, {wxStyledTextCtrl, setWhitespaceChars, 1}},
- {3371, {wxStyledTextCtrl, setCharsDefault, 0}},
- {3372, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
- {3373, {wxStyledTextCtrl, allocate, 1}},
- {3374, {wxStyledTextCtrl, findColumn, 2}},
- {3375, {wxStyledTextCtrl, getCaretSticky, 0}},
- {3376, {wxStyledTextCtrl, setCaretSticky, 1}},
- {3377, {wxStyledTextCtrl, toggleCaretSticky, 0}},
- {3378, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
- {3379, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
- {3380, {wxStyledTextCtrl, selectionDuplicate, 0}},
- {3381, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
- {3382, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
- {3383, {wxStyledTextCtrl, startRecord, 0}},
- {3384, {wxStyledTextCtrl, stopRecord, 0}},
- {3385, {wxStyledTextCtrl, setLexer, 1}},
- {3386, {wxStyledTextCtrl, getLexer, 0}},
- {3387, {wxStyledTextCtrl, colourise, 2}},
- {3388, {wxStyledTextCtrl, setProperty, 2}},
- {3389, {wxStyledTextCtrl, setKeyWords, 2}},
- {3390, {wxStyledTextCtrl, setLexerLanguage, 1}},
- {3391, {wxStyledTextCtrl, getProperty, 1}},
- {3392, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
- {3393, {wxStyledTextCtrl, getCurrentLine, 0}},
- {3394, {wxStyledTextCtrl, styleSetSpec, 2}},
- {3395, {wxStyledTextCtrl, styleSetFont, 2}},
- {3396, {wxStyledTextCtrl, styleSetFontAttr, 7}},
- {3397, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
- {3398, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
- {3399, {wxStyledTextCtrl, cmdKeyExecute, 1}},
- {3400, {wxStyledTextCtrl, setMargins, 2}},
- {3401, {wxStyledTextCtrl, getSelection, 2}},
- {3402, {wxStyledTextCtrl, pointFromPosition, 1}},
- {3403, {wxStyledTextCtrl, scrollToLine, 1}},
- {3404, {wxStyledTextCtrl, scrollToColumn, 1}},
- {3405, {wxStyledTextCtrl, setVScrollBar, 1}},
- {3406, {wxStyledTextCtrl, setHScrollBar, 1}},
- {3407, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
- {3408, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
- {3409, {wxStyledTextCtrl, saveFile, 1}},
- {3410, {wxStyledTextCtrl, loadFile, 1}},
- {3411, {wxStyledTextCtrl, doDragOver, 3}},
- {3412, {wxStyledTextCtrl, doDropText, 3}},
- {3413, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
- {3414, {wxStyledTextCtrl, addTextRaw, 1}},
- {3415, {wxStyledTextCtrl, insertTextRaw, 2}},
- {3416, {wxStyledTextCtrl, getCurLineRaw, 1}},
- {3417, {wxStyledTextCtrl, getLineRaw, 1}},
- {3418, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
- {3419, {wxStyledTextCtrl, getTextRangeRaw, 2}},
- {3420, {wxStyledTextCtrl, setTextRaw, 1}},
- {3421, {wxStyledTextCtrl, getTextRaw, 0}},
- {3422, {wxStyledTextCtrl, appendTextRaw, 1}},
- {3423, {wxArtProvider, getBitmap, 2}},
- {3424, {wxArtProvider, getIcon, 2}},
- {3425, {wxTreeEvent, getKeyCode, 0}},
- {3426, {wxTreeEvent, getItem, 0}},
- {3427, {wxTreeEvent, getKeyEvent, 0}},
- {3428, {wxTreeEvent, getLabel, 0}},
- {3429, {wxTreeEvent, getOldItem, 0}},
- {3430, {wxTreeEvent, getPoint, 0}},
- {3431, {wxTreeEvent, isEditCancelled, 0}},
- {3432, {wxTreeEvent, setToolTip, 1}},
- {3433, {wxNotebookEvent, getOldSelection, 0}},
- {3434, {wxNotebookEvent, getSelection, 0}},
- {3435, {wxNotebookEvent, setOldSelection, 1}},
- {3436, {wxNotebookEvent, setSelection, 1}},
- {3437, {wxFileDataObject, new, 0}},
- {3438, {wxFileDataObject, addFile, 1}},
- {3439, {wxFileDataObject, getFilenames, 0}},
- {3440, {wxFileDataObject, 'Destroy', undefined}},
- {3441, {wxTextDataObject, new, 1}},
- {3442, {wxTextDataObject, getTextLength, 0}},
- {3443, {wxTextDataObject, getText, 0}},
- {3444, {wxTextDataObject, setText, 1}},
- {3445, {wxTextDataObject, 'Destroy', undefined}},
- {3446, {wxBitmapDataObject, new_1_1, 1}},
- {3447, {wxBitmapDataObject, new_1_0, 1}},
- {3448, {wxBitmapDataObject, getBitmap, 0}},
- {3449, {wxBitmapDataObject, setBitmap, 1}},
- {3450, {wxBitmapDataObject, 'Destroy', undefined}},
- {3452, {wxClipboard, new, 0}},
- {3453, {wxClipboard, destruct, 0}},
- {3454, {wxClipboard, addData, 1}},
- {3455, {wxClipboard, clear, 0}},
- {3456, {wxClipboard, close, 0}},
- {3457, {wxClipboard, flush, 0}},
- {3458, {wxClipboard, getData, 1}},
- {3459, {wxClipboard, isOpened, 0}},
- {3460, {wxClipboard, open, 0}},
- {3461, {wxClipboard, setData, 1}},
- {3463, {wxClipboard, usePrimarySelection, 1}},
- {3464, {wxClipboard, isSupported, 1}},
- {3465, {wxClipboard, get, 0}},
- {3466, {wxSpinEvent, getPosition, 0}},
- {3467, {wxSpinEvent, setPosition, 1}},
- {3468, {wxSplitterWindow, new_0, 0}},
- {3469, {wxSplitterWindow, new_2, 2}},
- {3470, {wxSplitterWindow, destruct, 0}},
- {3471, {wxSplitterWindow, create, 2}},
- {3472, {wxSplitterWindow, getMinimumPaneSize, 0}},
- {3473, {wxSplitterWindow, getSashGravity, 0}},
- {3474, {wxSplitterWindow, getSashPosition, 0}},
- {3475, {wxSplitterWindow, getSplitMode, 0}},
- {3476, {wxSplitterWindow, getWindow1, 0}},
- {3477, {wxSplitterWindow, getWindow2, 0}},
- {3478, {wxSplitterWindow, initialize, 1}},
- {3479, {wxSplitterWindow, isSplit, 0}},
- {3480, {wxSplitterWindow, replaceWindow, 2}},
- {3481, {wxSplitterWindow, setSashGravity, 1}},
- {3482, {wxSplitterWindow, setSashPosition, 2}},
- {3483, {wxSplitterWindow, setSashSize, 1}},
- {3484, {wxSplitterWindow, setMinimumPaneSize, 1}},
- {3485, {wxSplitterWindow, setSplitMode, 1}},
- {3486, {wxSplitterWindow, splitHorizontally, 3}},
- {3487, {wxSplitterWindow, splitVertically, 3}},
- {3488, {wxSplitterWindow, unsplit, 1}},
- {3489, {wxSplitterWindow, updateSize, 0}},
- {3490, {wxSplitterEvent, getSashPosition, 0}},
- {3491, {wxSplitterEvent, getX, 0}},
- {3492, {wxSplitterEvent, getY, 0}},
- {3493, {wxSplitterEvent, getWindowBeingRemoved, 0}},
- {3494, {wxSplitterEvent, setSashPosition, 1}},
- {3495, {wxHtmlWindow, new_0, 0}},
- {3496, {wxHtmlWindow, new_2, 2}},
- {3497, {wxHtmlWindow, appendToPage, 1}},
- {3498, {wxHtmlWindow, getOpenedAnchor, 0}},
- {3499, {wxHtmlWindow, getOpenedPage, 0}},
- {3500, {wxHtmlWindow, getOpenedPageTitle, 0}},
- {3501, {wxHtmlWindow, getRelatedFrame, 0}},
- {3502, {wxHtmlWindow, historyBack, 0}},
- {3503, {wxHtmlWindow, historyCanBack, 0}},
- {3504, {wxHtmlWindow, historyCanForward, 0}},
- {3505, {wxHtmlWindow, historyClear, 0}},
- {3506, {wxHtmlWindow, historyForward, 0}},
- {3507, {wxHtmlWindow, loadFile, 1}},
- {3508, {wxHtmlWindow, loadPage, 1}},
- {3509, {wxHtmlWindow, selectAll, 0}},
- {3510, {wxHtmlWindow, selectionToText, 0}},
- {3511, {wxHtmlWindow, selectLine, 1}},
- {3512, {wxHtmlWindow, selectWord, 1}},
- {3513, {wxHtmlWindow, setBorders, 1}},
- {3514, {wxHtmlWindow, setFonts, 3}},
- {3515, {wxHtmlWindow, setPage, 1}},
- {3516, {wxHtmlWindow, setRelatedFrame, 2}},
- {3517, {wxHtmlWindow, setRelatedStatusBar, 1}},
- {3518, {wxHtmlWindow, toText, 0}},
- {3519, {wxHtmlWindow, 'Destroy', undefined}},
- {3520, {wxHtmlLinkEvent, getLinkInfo, 0}},
- {3521, {wxSystemSettings, getColour, 1}},
- {3522, {wxSystemSettings, getFont, 1}},
- {3523, {wxSystemSettings, getMetric, 2}},
- {3524, {wxSystemSettings, getScreenType, 0}},
- {3525, {wxSystemOptions, getOption, 1}},
- {3526, {wxSystemOptions, getOptionInt, 1}},
- {3527, {wxSystemOptions, hasOption, 1}},
- {3528, {wxSystemOptions, isFalse, 1}},
- {3529, {wxSystemOptions, setOption_2_1, 2}},
- {3530, {wxSystemOptions, setOption_2_0, 2}},
- {3531, {wxAuiNotebookEvent, setSelection, 1}},
- {3532, {wxAuiNotebookEvent, getSelection, 0}},
- {3533, {wxAuiNotebookEvent, setOldSelection, 1}},
- {3534, {wxAuiNotebookEvent, getOldSelection, 0}},
- {3535, {wxAuiNotebookEvent, setDragSource, 1}},
- {3536, {wxAuiNotebookEvent, getDragSource, 0}},
- {3537, {wxAuiManagerEvent, setManager, 1}},
- {3538, {wxAuiManagerEvent, getManager, 0}},
- {3539, {wxAuiManagerEvent, setPane, 1}},
- {3540, {wxAuiManagerEvent, getPane, 0}},
- {3541, {wxAuiManagerEvent, setButton, 1}},
- {3542, {wxAuiManagerEvent, getButton, 0}},
- {3543, {wxAuiManagerEvent, setDC, 1}},
- {3544, {wxAuiManagerEvent, getDC, 0}},
- {3545, {wxAuiManagerEvent, veto, 1}},
- {3546, {wxAuiManagerEvent, getVeto, 0}},
- {3547, {wxAuiManagerEvent, setCanVeto, 1}},
- {3548, {wxAuiManagerEvent, canVeto, 0}},
- {3549, {wxLogNull, new, 0}},
- {3550, {wxLogNull, 'Destroy', undefined}},
- {3551, {wxTaskBarIcon, new, 0}},
- {3552, {wxTaskBarIcon, destruct, 0}},
- {3553, {wxTaskBarIcon, popupMenu, 1}},
- {3554, {wxTaskBarIcon, removeIcon, 0}},
- {3555, {wxTaskBarIcon, setIcon, 2}},
- {3556, {wxLocale, new_0, 0}},
- {3558, {wxLocale, new_2, 2}},
- {3559, {wxLocale, destruct, 0}},
- {3561, {wxLocale, init, 1}},
- {3562, {wxLocale, addCatalog_1, 1}},
- {3563, {wxLocale, addCatalog_3, 3}},
- {3564, {wxLocale, addCatalogLookupPathPrefix, 1}},
- {3565, {wxLocale, getCanonicalName, 0}},
- {3566, {wxLocale, getLanguage, 0}},
- {3567, {wxLocale, getLanguageName, 1}},
- {3568, {wxLocale, getLocale, 0}},
- {3569, {wxLocale, getName, 0}},
- {3570, {wxLocale, getString_2, 2}},
- {3571, {wxLocale, getString_4, 4}},
- {3572, {wxLocale, getHeaderValue, 2}},
- {3573, {wxLocale, getSysName, 0}},
- {3574, {wxLocale, getSystemEncoding, 0}},
- {3575, {wxLocale, getSystemEncodingName, 0}},
- {3576, {wxLocale, getSystemLanguage, 0}},
- {3577, {wxLocale, isLoaded, 1}},
- {3578, {wxLocale, isOk, 0}},
- {3579, {wxActivateEvent, getActive, 0}},
- {3581, {wxPopupWindow, new_2, 2}},
- {3582, {wxPopupWindow, new_0, 0}},
- {3584, {wxPopupWindow, destruct, 0}},
- {3585, {wxPopupWindow, create, 2}},
- {3586, {wxPopupWindow, position, 2}},
- {3587, {wxPopupTransientWindow, new_0, 0}},
- {3588, {wxPopupTransientWindow, new_2, 2}},
- {3589, {wxPopupTransientWindow, destruct, 0}},
- {3590, {wxPopupTransientWindow, popup, 1}},
- {3591, {wxPopupTransientWindow, dismiss, 0}},
- {3592, {wxOverlay, new, 0}},
- {3593, {wxOverlay, destruct, 0}},
- {3594, {wxOverlay, reset, 0}},
- {3595, {wxDCOverlay, new_6, 6}},
- {3596, {wxDCOverlay, new_2, 2}},
- {3597, {wxDCOverlay, destruct, 0}},
- {3598, {wxDCOverlay, clear, 0}},
+ {1605, {wxGauge, getRange, 0}},
+ {1606, {wxGauge, getValue, 0}},
+ {1607, {wxGauge, isVertical, 0}},
+ {1608, {wxGauge, setRange, 1}},
+ {1609, {wxGauge, setValue, 1}},
+ {1610, {wxGauge, pulse, 0}},
+ {1611, {wxGauge, 'Destroy', undefined}},
+ {1612, {wxGenericDirCtrl, new_0, 0}},
+ {1613, {wxGenericDirCtrl, new_2, 2}},
+ {1614, {wxGenericDirCtrl, destruct, 0}},
+ {1615, {wxGenericDirCtrl, create, 2}},
+ {1616, {wxGenericDirCtrl, init, 0}},
+ {1617, {wxGenericDirCtrl, collapseTree, 0}},
+ {1618, {wxGenericDirCtrl, expandPath, 1}},
+ {1619, {wxGenericDirCtrl, getDefaultPath, 0}},
+ {1620, {wxGenericDirCtrl, getPath, 0}},
+ {1621, {wxGenericDirCtrl, getFilePath, 0}},
+ {1622, {wxGenericDirCtrl, getFilter, 0}},
+ {1623, {wxGenericDirCtrl, getFilterIndex, 0}},
+ {1624, {wxGenericDirCtrl, getRootId, 0}},
+ {1625, {wxGenericDirCtrl, getTreeCtrl, 0}},
+ {1626, {wxGenericDirCtrl, reCreateTree, 0}},
+ {1627, {wxGenericDirCtrl, setDefaultPath, 1}},
+ {1628, {wxGenericDirCtrl, setFilter, 1}},
+ {1629, {wxGenericDirCtrl, setFilterIndex, 1}},
+ {1630, {wxGenericDirCtrl, setPath, 1}},
+ {1632, {wxStaticBox, new_4, 4}},
+ {1633, {wxStaticBox, new_0, 0}},
+ {1634, {wxStaticBox, create, 4}},
+ {1635, {wxStaticBox, 'Destroy', undefined}},
+ {1637, {wxStaticLine, new_2, 2}},
+ {1638, {wxStaticLine, new_0, 0}},
+ {1639, {wxStaticLine, create, 2}},
+ {1640, {wxStaticLine, isVertical, 0}},
+ {1641, {wxStaticLine, getDefaultSize, 0}},
+ {1642, {wxStaticLine, 'Destroy', undefined}},
+ {1645, {wxListBox, new_3, 3}},
+ {1646, {wxListBox, new_0, 0}},
+ {1648, {wxListBox, destruct, 0}},
+ {1650, {wxListBox, create, 6}},
+ {1651, {wxListBox, deselect, 1}},
+ {1652, {wxListBox, getSelections, 1}},
+ {1653, {wxListBox, insertItems, 2}},
+ {1654, {wxListBox, isSelected, 1}},
+ {1655, {wxListBox, set, 1}},
+ {1656, {wxListBox, hitTest, 1}},
+ {1657, {wxListBox, setFirstItem_1_0, 1}},
+ {1658, {wxListBox, setFirstItem_1_1, 1}},
+ {1659, {wxListCtrl, new_0, 0}},
+ {1660, {wxListCtrl, new_2, 2}},
+ {1661, {wxListCtrl, arrange, 1}},
+ {1662, {wxListCtrl, assignImageList, 2}},
+ {1663, {wxListCtrl, clearAll, 0}},
+ {1664, {wxListCtrl, create, 2}},
+ {1665, {wxListCtrl, deleteAllItems, 0}},
+ {1666, {wxListCtrl, deleteColumn, 1}},
+ {1667, {wxListCtrl, deleteItem, 1}},
+ {1668, {wxListCtrl, editLabel, 1}},
+ {1669, {wxListCtrl, ensureVisible, 1}},
+ {1670, {wxListCtrl, findItem_3_0, 3}},
+ {1671, {wxListCtrl, findItem_3_1, 3}},
+ {1672, {wxListCtrl, getColumn, 2}},
+ {1673, {wxListCtrl, getColumnCount, 0}},
+ {1674, {wxListCtrl, getColumnWidth, 1}},
+ {1675, {wxListCtrl, getCountPerPage, 0}},
+ {1676, {wxListCtrl, getEditControl, 0}},
+ {1677, {wxListCtrl, getImageList, 1}},
+ {1678, {wxListCtrl, getItem, 1}},
+ {1679, {wxListCtrl, getItemBackgroundColour, 1}},
+ {1680, {wxListCtrl, getItemCount, 0}},
+ {1681, {wxListCtrl, getItemData, 1}},
+ {1682, {wxListCtrl, getItemFont, 1}},
+ {1683, {wxListCtrl, getItemPosition, 2}},
+ {1684, {wxListCtrl, getItemRect, 3}},
+ {1685, {wxListCtrl, getItemSpacing, 0}},
+ {1686, {wxListCtrl, getItemState, 2}},
+ {1687, {wxListCtrl, getItemText, 1}},
+ {1688, {wxListCtrl, getItemTextColour, 1}},
+ {1689, {wxListCtrl, getNextItem, 2}},
+ {1690, {wxListCtrl, getSelectedItemCount, 0}},
+ {1691, {wxListCtrl, getTextColour, 0}},
+ {1692, {wxListCtrl, getTopItem, 0}},
+ {1693, {wxListCtrl, getViewRect, 0}},
+ {1694, {wxListCtrl, hitTest, 3}},
+ {1695, {wxListCtrl, insertColumn_2, 2}},
+ {1696, {wxListCtrl, insertColumn_3, 3}},
+ {1697, {wxListCtrl, insertItem_1, 1}},
+ {1698, {wxListCtrl, insertItem_2_1, 2}},
+ {1699, {wxListCtrl, insertItem_2_0, 2}},
+ {1700, {wxListCtrl, insertItem_3, 3}},
+ {1701, {wxListCtrl, refreshItem, 1}},
+ {1702, {wxListCtrl, refreshItems, 2}},
+ {1703, {wxListCtrl, scrollList, 2}},
+ {1704, {wxListCtrl, setBackgroundColour, 1}},
+ {1705, {wxListCtrl, setColumn, 2}},
+ {1706, {wxListCtrl, setColumnWidth, 2}},
+ {1707, {wxListCtrl, setImageList, 2}},
+ {1708, {wxListCtrl, setItem_1, 1}},
+ {1709, {wxListCtrl, setItem_4, 4}},
+ {1710, {wxListCtrl, setItemBackgroundColour, 2}},
+ {1711, {wxListCtrl, setItemCount, 1}},
+ {1712, {wxListCtrl, setItemData, 2}},
+ {1713, {wxListCtrl, setItemFont, 2}},
+ {1714, {wxListCtrl, setItemImage, 3}},
+ {1715, {wxListCtrl, setItemColumnImage, 3}},
+ {1716, {wxListCtrl, setItemPosition, 2}},
+ {1717, {wxListCtrl, setItemState, 3}},
+ {1718, {wxListCtrl, setItemText, 2}},
+ {1719, {wxListCtrl, setItemTextColour, 2}},
+ {1720, {wxListCtrl, setSingleStyle, 2}},
+ {1721, {wxListCtrl, setTextColour, 1}},
+ {1722, {wxListCtrl, setWindowStyleFlag, 1}},
+ {1723, {wxListCtrl, sortItems, 2}},
+ {1724, {wxListCtrl, 'Destroy', undefined}},
+ {1725, {wxListView, clearColumnImage, 1}},
+ {1726, {wxListView, focus, 1}},
+ {1727, {wxListView, getFirstSelected, 0}},
+ {1728, {wxListView, getFocusedItem, 0}},
+ {1729, {wxListView, getNextSelected, 1}},
+ {1730, {wxListView, isSelected, 1}},
+ {1731, {wxListView, select, 2}},
+ {1732, {wxListView, setColumnImage, 2}},
+ {1733, {wxListItem, new_0, 0}},
+ {1734, {wxListItem, new_1, 1}},
+ {1735, {wxListItem, destruct, 0}},
+ {1736, {wxListItem, clear, 0}},
+ {1737, {wxListItem, getAlign, 0}},
+ {1738, {wxListItem, getBackgroundColour, 0}},
+ {1739, {wxListItem, getColumn, 0}},
+ {1740, {wxListItem, getFont, 0}},
+ {1741, {wxListItem, getId, 0}},
+ {1742, {wxListItem, getImage, 0}},
+ {1743, {wxListItem, getMask, 0}},
+ {1744, {wxListItem, getState, 0}},
+ {1745, {wxListItem, getText, 0}},
+ {1746, {wxListItem, getTextColour, 0}},
+ {1747, {wxListItem, getWidth, 0}},
+ {1748, {wxListItem, setAlign, 1}},
+ {1749, {wxListItem, setBackgroundColour, 1}},
+ {1750, {wxListItem, setColumn, 1}},
+ {1751, {wxListItem, setFont, 1}},
+ {1752, {wxListItem, setId, 1}},
+ {1753, {wxListItem, setImage, 1}},
+ {1754, {wxListItem, setMask, 1}},
+ {1755, {wxListItem, setState, 1}},
+ {1756, {wxListItem, setStateMask, 1}},
+ {1757, {wxListItem, setText, 1}},
+ {1758, {wxListItem, setTextColour, 1}},
+ {1759, {wxListItem, setWidth, 1}},
+ {1760, {wxListItemAttr, new_0, 0}},
+ {1761, {wxListItemAttr, new_3, 3}},
+ {1762, {wxListItemAttr, getBackgroundColour, 0}},
+ {1763, {wxListItemAttr, getFont, 0}},
+ {1764, {wxListItemAttr, getTextColour, 0}},
+ {1765, {wxListItemAttr, hasBackgroundColour, 0}},
+ {1766, {wxListItemAttr, hasFont, 0}},
+ {1767, {wxListItemAttr, hasTextColour, 0}},
+ {1768, {wxListItemAttr, setBackgroundColour, 1}},
+ {1769, {wxListItemAttr, setFont, 1}},
+ {1770, {wxListItemAttr, setTextColour, 1}},
+ {1771, {wxListItemAttr, 'Destroy', undefined}},
+ {1772, {wxImageList, new_0, 0}},
+ {1773, {wxImageList, new_3, 3}},
+ {1774, {wxImageList, add_1, 1}},
+ {1775, {wxImageList, add_2_0, 2}},
+ {1776, {wxImageList, add_2_1, 2}},
+ {1777, {wxImageList, create, 3}},
+ {1779, {wxImageList, draw, 5}},
+ {1780, {wxImageList, getBitmap, 1}},
+ {1781, {wxImageList, getIcon, 1}},
+ {1782, {wxImageList, getImageCount, 0}},
+ {1783, {wxImageList, getSize, 3}},
+ {1784, {wxImageList, remove, 1}},
+ {1785, {wxImageList, removeAll, 0}},
+ {1786, {wxImageList, replace_2, 2}},
+ {1787, {wxImageList, replace_3, 3}},
+ {1788, {wxImageList, 'Destroy', undefined}},
+ {1789, {wxTextAttr, new_0, 0}},
+ {1790, {wxTextAttr, new_2, 2}},
+ {1791, {wxTextAttr, getAlignment, 0}},
+ {1792, {wxTextAttr, getBackgroundColour, 0}},
+ {1793, {wxTextAttr, getFont, 0}},
+ {1794, {wxTextAttr, getLeftIndent, 0}},
+ {1795, {wxTextAttr, getLeftSubIndent, 0}},
+ {1796, {wxTextAttr, getRightIndent, 0}},
+ {1797, {wxTextAttr, getTabs, 0}},
+ {1798, {wxTextAttr, getTextColour, 0}},
+ {1799, {wxTextAttr, hasBackgroundColour, 0}},
+ {1800, {wxTextAttr, hasFont, 0}},
+ {1801, {wxTextAttr, hasTextColour, 0}},
+ {1802, {wxTextAttr, getFlags, 0}},
+ {1803, {wxTextAttr, isDefault, 0}},
+ {1804, {wxTextAttr, setAlignment, 1}},
+ {1805, {wxTextAttr, setBackgroundColour, 1}},
+ {1806, {wxTextAttr, setFlags, 1}},
+ {1807, {wxTextAttr, setFont, 2}},
+ {1808, {wxTextAttr, setLeftIndent, 2}},
+ {1809, {wxTextAttr, setRightIndent, 1}},
+ {1810, {wxTextAttr, setTabs, 1}},
+ {1811, {wxTextAttr, setTextColour, 1}},
+ {1812, {wxTextAttr, 'Destroy', undefined}},
+ {1814, {wxTextCtrl, new_3, 3}},
+ {1815, {wxTextCtrl, new_0, 0}},
+ {1817, {wxTextCtrl, destruct, 0}},
+ {1818, {wxTextCtrl, appendText, 1}},
+ {1819, {wxTextCtrl, canCopy, 0}},
+ {1820, {wxTextCtrl, canCut, 0}},
+ {1821, {wxTextCtrl, canPaste, 0}},
+ {1822, {wxTextCtrl, canRedo, 0}},
+ {1823, {wxTextCtrl, canUndo, 0}},
+ {1824, {wxTextCtrl, clear, 0}},
+ {1825, {wxTextCtrl, copy, 0}},
+ {1826, {wxTextCtrl, create, 3}},
+ {1827, {wxTextCtrl, cut, 0}},
+ {1828, {wxTextCtrl, discardEdits, 0}},
+ {1829, {wxTextCtrl, changeValue, 1}},
+ {1830, {wxTextCtrl, emulateKeyPress, 1}},
+ {1831, {wxTextCtrl, getDefaultStyle, 0}},
+ {1832, {wxTextCtrl, getInsertionPoint, 0}},
+ {1833, {wxTextCtrl, getLastPosition, 0}},
+ {1834, {wxTextCtrl, getLineLength, 1}},
+ {1835, {wxTextCtrl, getLineText, 1}},
+ {1836, {wxTextCtrl, getNumberOfLines, 0}},
+ {1837, {wxTextCtrl, getRange, 2}},
+ {1838, {wxTextCtrl, getSelection, 2}},
+ {1839, {wxTextCtrl, getStringSelection, 0}},
+ {1840, {wxTextCtrl, getStyle, 2}},
+ {1841, {wxTextCtrl, getValue, 0}},
+ {1842, {wxTextCtrl, isEditable, 0}},
+ {1843, {wxTextCtrl, isModified, 0}},
+ {1844, {wxTextCtrl, isMultiLine, 0}},
+ {1845, {wxTextCtrl, isSingleLine, 0}},
+ {1846, {wxTextCtrl, loadFile, 2}},
+ {1847, {wxTextCtrl, markDirty, 0}},
+ {1848, {wxTextCtrl, paste, 0}},
+ {1849, {wxTextCtrl, positionToXY, 3}},
+ {1850, {wxTextCtrl, redo, 0}},
+ {1851, {wxTextCtrl, remove, 2}},
+ {1852, {wxTextCtrl, replace, 3}},
+ {1853, {wxTextCtrl, saveFile, 1}},
+ {1854, {wxTextCtrl, setDefaultStyle, 1}},
+ {1855, {wxTextCtrl, setEditable, 1}},
+ {1856, {wxTextCtrl, setInsertionPoint, 1}},
+ {1857, {wxTextCtrl, setInsertionPointEnd, 0}},
+ {1859, {wxTextCtrl, setMaxLength, 1}},
+ {1860, {wxTextCtrl, setSelection, 2}},
+ {1861, {wxTextCtrl, setStyle, 3}},
+ {1862, {wxTextCtrl, setValue, 1}},
+ {1863, {wxTextCtrl, showPosition, 1}},
+ {1864, {wxTextCtrl, undo, 0}},
+ {1865, {wxTextCtrl, writeText, 1}},
+ {1866, {wxTextCtrl, xYToPosition, 2}},
+ {1869, {wxNotebook, new_0, 0}},
+ {1870, {wxNotebook, new_3, 3}},
+ {1871, {wxNotebook, destruct, 0}},
+ {1872, {wxNotebook, addPage, 3}},
+ {1873, {wxNotebook, advanceSelection, 1}},
+ {1874, {wxNotebook, assignImageList, 1}},
+ {1875, {wxNotebook, create, 3}},
+ {1876, {wxNotebook, deleteAllPages, 0}},
+ {1877, {wxNotebook, deletePage, 1}},
+ {1878, {wxNotebook, removePage, 1}},
+ {1879, {wxNotebook, getCurrentPage, 0}},
+ {1880, {wxNotebook, getImageList, 0}},
+ {1882, {wxNotebook, getPage, 1}},
+ {1883, {wxNotebook, getPageCount, 0}},
+ {1884, {wxNotebook, getPageImage, 1}},
+ {1885, {wxNotebook, getPageText, 1}},
+ {1886, {wxNotebook, getRowCount, 0}},
+ {1887, {wxNotebook, getSelection, 0}},
+ {1888, {wxNotebook, getThemeBackgroundColour, 0}},
+ {1890, {wxNotebook, hitTest, 2}},
+ {1892, {wxNotebook, insertPage, 4}},
+ {1893, {wxNotebook, setImageList, 1}},
+ {1894, {wxNotebook, setPadding, 1}},
+ {1895, {wxNotebook, setPageSize, 1}},
+ {1896, {wxNotebook, setPageImage, 2}},
+ {1897, {wxNotebook, setPageText, 2}},
+ {1898, {wxNotebook, setSelection, 1}},
+ {1899, {wxNotebook, changeSelection, 1}},
+ {1900, {wxChoicebook, new_0, 0}},
+ {1901, {wxChoicebook, new_3, 3}},
+ {1902, {wxChoicebook, addPage, 3}},
+ {1903, {wxChoicebook, advanceSelection, 1}},
+ {1904, {wxChoicebook, assignImageList, 1}},
+ {1905, {wxChoicebook, create, 3}},
+ {1906, {wxChoicebook, deleteAllPages, 0}},
+ {1907, {wxChoicebook, deletePage, 1}},
+ {1908, {wxChoicebook, removePage, 1}},
+ {1909, {wxChoicebook, getCurrentPage, 0}},
+ {1910, {wxChoicebook, getImageList, 0}},
+ {1912, {wxChoicebook, getPage, 1}},
+ {1913, {wxChoicebook, getPageCount, 0}},
+ {1914, {wxChoicebook, getPageImage, 1}},
+ {1915, {wxChoicebook, getPageText, 1}},
+ {1916, {wxChoicebook, getSelection, 0}},
+ {1917, {wxChoicebook, hitTest, 2}},
+ {1918, {wxChoicebook, insertPage, 4}},
+ {1919, {wxChoicebook, setImageList, 1}},
+ {1920, {wxChoicebook, setPageSize, 1}},
+ {1921, {wxChoicebook, setPageImage, 2}},
+ {1922, {wxChoicebook, setPageText, 2}},
+ {1923, {wxChoicebook, setSelection, 1}},
+ {1924, {wxChoicebook, changeSelection, 1}},
+ {1925, {wxChoicebook, 'Destroy', undefined}},
+ {1926, {wxToolbook, new_0, 0}},
+ {1927, {wxToolbook, new_3, 3}},
+ {1928, {wxToolbook, addPage, 3}},
+ {1929, {wxToolbook, advanceSelection, 1}},
+ {1930, {wxToolbook, assignImageList, 1}},
+ {1931, {wxToolbook, create, 3}},
+ {1932, {wxToolbook, deleteAllPages, 0}},
+ {1933, {wxToolbook, deletePage, 1}},
+ {1934, {wxToolbook, removePage, 1}},
+ {1935, {wxToolbook, getCurrentPage, 0}},
+ {1936, {wxToolbook, getImageList, 0}},
+ {1938, {wxToolbook, getPage, 1}},
+ {1939, {wxToolbook, getPageCount, 0}},
+ {1940, {wxToolbook, getPageImage, 1}},
+ {1941, {wxToolbook, getPageText, 1}},
+ {1942, {wxToolbook, getSelection, 0}},
+ {1944, {wxToolbook, hitTest, 2}},
+ {1945, {wxToolbook, insertPage, 4}},
+ {1946, {wxToolbook, setImageList, 1}},
+ {1947, {wxToolbook, setPageSize, 1}},
+ {1948, {wxToolbook, setPageImage, 2}},
+ {1949, {wxToolbook, setPageText, 2}},
+ {1950, {wxToolbook, setSelection, 1}},
+ {1951, {wxToolbook, changeSelection, 1}},
+ {1952, {wxToolbook, 'Destroy', undefined}},
+ {1953, {wxListbook, new_0, 0}},
+ {1954, {wxListbook, new_3, 3}},
+ {1955, {wxListbook, addPage, 3}},
+ {1956, {wxListbook, advanceSelection, 1}},
+ {1957, {wxListbook, assignImageList, 1}},
+ {1958, {wxListbook, create, 3}},
+ {1959, {wxListbook, deleteAllPages, 0}},
+ {1960, {wxListbook, deletePage, 1}},
+ {1961, {wxListbook, removePage, 1}},
+ {1962, {wxListbook, getCurrentPage, 0}},
+ {1963, {wxListbook, getImageList, 0}},
+ {1965, {wxListbook, getPage, 1}},
+ {1966, {wxListbook, getPageCount, 0}},
+ {1967, {wxListbook, getPageImage, 1}},
+ {1968, {wxListbook, getPageText, 1}},
+ {1969, {wxListbook, getSelection, 0}},
+ {1971, {wxListbook, hitTest, 2}},
+ {1972, {wxListbook, insertPage, 4}},
+ {1973, {wxListbook, setImageList, 1}},
+ {1974, {wxListbook, setPageSize, 1}},
+ {1975, {wxListbook, setPageImage, 2}},
+ {1976, {wxListbook, setPageText, 2}},
+ {1977, {wxListbook, setSelection, 1}},
+ {1978, {wxListbook, changeSelection, 1}},
+ {1979, {wxListbook, 'Destroy', undefined}},
+ {1980, {wxTreebook, new_0, 0}},
+ {1981, {wxTreebook, new_3, 3}},
+ {1982, {wxTreebook, addPage, 3}},
+ {1983, {wxTreebook, advanceSelection, 1}},
+ {1984, {wxTreebook, assignImageList, 1}},
+ {1985, {wxTreebook, create, 3}},
+ {1986, {wxTreebook, deleteAllPages, 0}},
+ {1987, {wxTreebook, deletePage, 1}},
+ {1988, {wxTreebook, removePage, 1}},
+ {1989, {wxTreebook, getCurrentPage, 0}},
+ {1990, {wxTreebook, getImageList, 0}},
+ {1992, {wxTreebook, getPage, 1}},
+ {1993, {wxTreebook, getPageCount, 0}},
+ {1994, {wxTreebook, getPageImage, 1}},
+ {1995, {wxTreebook, getPageText, 1}},
+ {1996, {wxTreebook, getSelection, 0}},
+ {1997, {wxTreebook, expandNode, 2}},
+ {1998, {wxTreebook, isNodeExpanded, 1}},
+ {2000, {wxTreebook, hitTest, 2}},
+ {2001, {wxTreebook, insertPage, 4}},
+ {2002, {wxTreebook, insertSubPage, 4}},
+ {2003, {wxTreebook, setImageList, 1}},
+ {2004, {wxTreebook, setPageSize, 1}},
+ {2005, {wxTreebook, setPageImage, 2}},
+ {2006, {wxTreebook, setPageText, 2}},
+ {2007, {wxTreebook, setSelection, 1}},
+ {2008, {wxTreebook, changeSelection, 1}},
+ {2009, {wxTreebook, 'Destroy', undefined}},
+ {2012, {wxTreeCtrl, new_2, 2}},
+ {2013, {wxTreeCtrl, new_0, 0}},
+ {2015, {wxTreeCtrl, destruct, 0}},
+ {2016, {wxTreeCtrl, addRoot, 2}},
+ {2017, {wxTreeCtrl, appendItem, 3}},
+ {2018, {wxTreeCtrl, assignImageList, 1}},
+ {2019, {wxTreeCtrl, assignStateImageList, 1}},
+ {2020, {wxTreeCtrl, collapse, 1}},
+ {2021, {wxTreeCtrl, collapseAndReset, 1}},
+ {2022, {wxTreeCtrl, create, 2}},
+ {2023, {wxTreeCtrl, delete, 1}},
+ {2024, {wxTreeCtrl, deleteAllItems, 0}},
+ {2025, {wxTreeCtrl, deleteChildren, 1}},
+ {2026, {wxTreeCtrl, editLabel, 1}},
+ {2027, {wxTreeCtrl, ensureVisible, 1}},
+ {2028, {wxTreeCtrl, expand, 1}},
+ {2029, {wxTreeCtrl, getBoundingRect, 3}},
+ {2031, {wxTreeCtrl, getChildrenCount, 2}},
+ {2032, {wxTreeCtrl, getCount, 0}},
+ {2033, {wxTreeCtrl, getEditControl, 0}},
+ {2034, {wxTreeCtrl, getFirstChild, 2}},
+ {2035, {wxTreeCtrl, getNextChild, 2}},
+ {2036, {wxTreeCtrl, getFirstVisibleItem, 0}},
+ {2037, {wxTreeCtrl, getImageList, 0}},
+ {2038, {wxTreeCtrl, getIndent, 0}},
+ {2039, {wxTreeCtrl, getItemBackgroundColour, 1}},
+ {2040, {wxTreeCtrl, getItemData, 1}},
+ {2041, {wxTreeCtrl, getItemFont, 1}},
+ {2042, {wxTreeCtrl, getItemImage_1, 1}},
+ {2043, {wxTreeCtrl, getItemImage_2, 2}},
+ {2044, {wxTreeCtrl, getItemText, 1}},
+ {2045, {wxTreeCtrl, getItemTextColour, 1}},
+ {2046, {wxTreeCtrl, getLastChild, 1}},
+ {2047, {wxTreeCtrl, getNextSibling, 1}},
+ {2048, {wxTreeCtrl, getNextVisible, 1}},
+ {2049, {wxTreeCtrl, getItemParent, 1}},
+ {2050, {wxTreeCtrl, getPrevSibling, 1}},
+ {2051, {wxTreeCtrl, getPrevVisible, 1}},
+ {2052, {wxTreeCtrl, getRootItem, 0}},
+ {2053, {wxTreeCtrl, getSelection, 0}},
+ {2054, {wxTreeCtrl, getSelections, 1}},
+ {2055, {wxTreeCtrl, getStateImageList, 0}},
+ {2056, {wxTreeCtrl, hitTest, 2}},
+ {2058, {wxTreeCtrl, insertItem, 4}},
+ {2059, {wxTreeCtrl, isBold, 1}},
+ {2060, {wxTreeCtrl, isExpanded, 1}},
+ {2061, {wxTreeCtrl, isSelected, 1}},
+ {2062, {wxTreeCtrl, isVisible, 1}},
+ {2063, {wxTreeCtrl, itemHasChildren, 1}},
+ {2064, {wxTreeCtrl, isTreeItemIdOk, 1}},
+ {2065, {wxTreeCtrl, prependItem, 3}},
+ {2066, {wxTreeCtrl, scrollTo, 1}},
+ {2067, {wxTreeCtrl, selectItem_1, 1}},
+ {2068, {wxTreeCtrl, selectItem_2, 2}},
+ {2069, {wxTreeCtrl, setIndent, 1}},
+ {2070, {wxTreeCtrl, setImageList, 1}},
+ {2071, {wxTreeCtrl, setItemBackgroundColour, 2}},
+ {2072, {wxTreeCtrl, setItemBold, 2}},
+ {2073, {wxTreeCtrl, setItemData, 2}},
+ {2074, {wxTreeCtrl, setItemDropHighlight, 2}},
+ {2075, {wxTreeCtrl, setItemFont, 2}},
+ {2076, {wxTreeCtrl, setItemHasChildren, 2}},
+ {2077, {wxTreeCtrl, setItemImage_2, 2}},
+ {2078, {wxTreeCtrl, setItemImage_3, 3}},
+ {2079, {wxTreeCtrl, setItemText, 2}},
+ {2080, {wxTreeCtrl, setItemTextColour, 2}},
+ {2081, {wxTreeCtrl, setStateImageList, 1}},
+ {2082, {wxTreeCtrl, setWindowStyle, 1}},
+ {2083, {wxTreeCtrl, sortChildren, 1}},
+ {2084, {wxTreeCtrl, toggle, 1}},
+ {2085, {wxTreeCtrl, toggleItemSelection, 1}},
+ {2086, {wxTreeCtrl, unselect, 0}},
+ {2087, {wxTreeCtrl, unselectAll, 0}},
+ {2088, {wxTreeCtrl, unselectItem, 1}},
+ {2089, {wxScrollBar, new_0, 0}},
+ {2090, {wxScrollBar, new_3, 3}},
+ {2091, {wxScrollBar, destruct, 0}},
+ {2092, {wxScrollBar, create, 3}},
+ {2093, {wxScrollBar, getRange, 0}},
+ {2094, {wxScrollBar, getPageSize, 0}},
+ {2095, {wxScrollBar, getThumbPosition, 0}},
+ {2096, {wxScrollBar, getThumbSize, 0}},
+ {2097, {wxScrollBar, setThumbPosition, 1}},
+ {2098, {wxScrollBar, setScrollbar, 5}},
+ {2100, {wxSpinButton, new_2, 2}},
+ {2101, {wxSpinButton, new_0, 0}},
+ {2102, {wxSpinButton, create, 2}},
+ {2103, {wxSpinButton, getMax, 0}},
+ {2104, {wxSpinButton, getMin, 0}},
+ {2105, {wxSpinButton, getValue, 0}},
+ {2106, {wxSpinButton, setRange, 2}},
+ {2107, {wxSpinButton, setValue, 1}},
+ {2108, {wxSpinButton, 'Destroy', undefined}},
+ {2109, {wxSpinCtrl, new_0, 0}},
+ {2110, {wxSpinCtrl, new_2, 2}},
+ {2112, {wxSpinCtrl, create, 2}},
+ {2115, {wxSpinCtrl, setValue_1_1, 1}},
+ {2116, {wxSpinCtrl, setValue_1_0, 1}},
+ {2118, {wxSpinCtrl, getValue, 0}},
+ {2120, {wxSpinCtrl, setRange, 2}},
+ {2121, {wxSpinCtrl, setSelection, 2}},
+ {2123, {wxSpinCtrl, getMin, 0}},
+ {2125, {wxSpinCtrl, getMax, 0}},
+ {2126, {wxSpinCtrl, 'Destroy', undefined}},
+ {2127, {wxStaticText, new_0, 0}},
+ {2128, {wxStaticText, new_4, 4}},
+ {2129, {wxStaticText, create, 4}},
+ {2130, {wxStaticText, getLabel, 0}},
+ {2131, {wxStaticText, setLabel, 1}},
+ {2132, {wxStaticText, wrap, 1}},
+ {2133, {wxStaticText, 'Destroy', undefined}},
+ {2134, {wxStaticBitmap, new_0, 0}},
+ {2135, {wxStaticBitmap, new_4, 4}},
+ {2136, {wxStaticBitmap, create, 4}},
+ {2137, {wxStaticBitmap, getBitmap, 0}},
+ {2138, {wxStaticBitmap, setBitmap, 1}},
+ {2139, {wxStaticBitmap, 'Destroy', undefined}},
+ {2140, {wxRadioBox, new, 7}},
+ {2142, {wxRadioBox, destruct, 0}},
+ {2143, {wxRadioBox, create, 7}},
+ {2144, {wxRadioBox, enable_2, 2}},
+ {2145, {wxRadioBox, enable_1, 1}},
+ {2146, {wxRadioBox, getSelection, 0}},
+ {2147, {wxRadioBox, getString, 1}},
+ {2148, {wxRadioBox, setSelection, 1}},
+ {2149, {wxRadioBox, show_2, 2}},
+ {2150, {wxRadioBox, show_1, 1}},
+ {2151, {wxRadioBox, getColumnCount, 0}},
+ {2152, {wxRadioBox, getItemHelpText, 1}},
+ {2153, {wxRadioBox, getItemToolTip, 1}},
+ {2155, {wxRadioBox, getItemFromPoint, 1}},
+ {2156, {wxRadioBox, getRowCount, 0}},
+ {2157, {wxRadioBox, isItemEnabled, 1}},
+ {2158, {wxRadioBox, isItemShown, 1}},
+ {2159, {wxRadioBox, setItemHelpText, 2}},
+ {2160, {wxRadioBox, setItemToolTip, 2}},
+ {2161, {wxRadioButton, new_0, 0}},
+ {2162, {wxRadioButton, new_4, 4}},
+ {2163, {wxRadioButton, create, 4}},
+ {2164, {wxRadioButton, getValue, 0}},
+ {2165, {wxRadioButton, setValue, 1}},
+ {2166, {wxRadioButton, 'Destroy', undefined}},
+ {2168, {wxSlider, new_6, 6}},
+ {2169, {wxSlider, new_0, 0}},
+ {2170, {wxSlider, create, 6}},
+ {2171, {wxSlider, getLineSize, 0}},
+ {2172, {wxSlider, getMax, 0}},
+ {2173, {wxSlider, getMin, 0}},
+ {2174, {wxSlider, getPageSize, 0}},
+ {2175, {wxSlider, getThumbLength, 0}},
+ {2176, {wxSlider, getValue, 0}},
+ {2177, {wxSlider, setLineSize, 1}},
+ {2178, {wxSlider, setPageSize, 1}},
+ {2179, {wxSlider, setRange, 2}},
+ {2180, {wxSlider, setThumbLength, 1}},
+ {2181, {wxSlider, setValue, 1}},
+ {2182, {wxSlider, 'Destroy', undefined}},
+ {2184, {wxDialog, new_4, 4}},
+ {2185, {wxDialog, new_0, 0}},
+ {2187, {wxDialog, destruct, 0}},
+ {2188, {wxDialog, create, 4}},
+ {2189, {wxDialog, createButtonSizer, 1}},
+ {2190, {wxDialog, createStdDialogButtonSizer, 1}},
+ {2191, {wxDialog, endModal, 1}},
+ {2192, {wxDialog, getAffirmativeId, 0}},
+ {2193, {wxDialog, getReturnCode, 0}},
+ {2194, {wxDialog, isModal, 0}},
+ {2195, {wxDialog, setAffirmativeId, 1}},
+ {2196, {wxDialog, setReturnCode, 1}},
+ {2197, {wxDialog, show, 1}},
+ {2198, {wxDialog, showModal, 0}},
+ {2199, {wxColourDialog, new_0, 0}},
+ {2200, {wxColourDialog, new_2, 2}},
+ {2201, {wxColourDialog, destruct, 0}},
+ {2202, {wxColourDialog, create, 2}},
+ {2203, {wxColourDialog, getColourData, 0}},
+ {2204, {wxColourData, new_0, 0}},
+ {2205, {wxColourData, new_1, 1}},
+ {2206, {wxColourData, destruct, 0}},
+ {2207, {wxColourData, getChooseFull, 0}},
+ {2208, {wxColourData, getColour, 0}},
+ {2210, {wxColourData, getCustomColour, 1}},
+ {2211, {wxColourData, setChooseFull, 1}},
+ {2212, {wxColourData, setColour, 1}},
+ {2213, {wxColourData, setCustomColour, 2}},
+ {2214, {wxPalette, new_0, 0}},
+ {2215, {wxPalette, new_4, 4}},
+ {2217, {wxPalette, destruct, 0}},
+ {2218, {wxPalette, create, 4}},
+ {2219, {wxPalette, getColoursCount, 0}},
+ {2220, {wxPalette, getPixel, 3}},
+ {2221, {wxPalette, getRGB, 4}},
+ {2222, {wxPalette, isOk, 0}},
+ {2226, {wxDirDialog, new, 2}},
+ {2227, {wxDirDialog, destruct, 0}},
+ {2228, {wxDirDialog, getPath, 0}},
+ {2229, {wxDirDialog, getMessage, 0}},
+ {2230, {wxDirDialog, setMessage, 1}},
+ {2231, {wxDirDialog, setPath, 1}},
+ {2235, {wxFileDialog, new, 2}},
+ {2236, {wxFileDialog, destruct, 0}},
+ {2237, {wxFileDialog, getDirectory, 0}},
+ {2238, {wxFileDialog, getFilename, 0}},
+ {2239, {wxFileDialog, getFilenames, 1}},
+ {2240, {wxFileDialog, getFilterIndex, 0}},
+ {2241, {wxFileDialog, getMessage, 0}},
+ {2242, {wxFileDialog, getPath, 0}},
+ {2243, {wxFileDialog, getPaths, 1}},
+ {2244, {wxFileDialog, getWildcard, 0}},
+ {2245, {wxFileDialog, setDirectory, 1}},
+ {2246, {wxFileDialog, setFilename, 1}},
+ {2247, {wxFileDialog, setFilterIndex, 1}},
+ {2248, {wxFileDialog, setMessage, 1}},
+ {2249, {wxFileDialog, setPath, 1}},
+ {2250, {wxFileDialog, setWildcard, 1}},
+ {2251, {wxPickerBase, setInternalMargin, 1}},
+ {2252, {wxPickerBase, getInternalMargin, 0}},
+ {2253, {wxPickerBase, setTextCtrlProportion, 1}},
+ {2254, {wxPickerBase, setPickerCtrlProportion, 1}},
+ {2255, {wxPickerBase, getTextCtrlProportion, 0}},
+ {2256, {wxPickerBase, getPickerCtrlProportion, 0}},
+ {2257, {wxPickerBase, hasTextCtrl, 0}},
+ {2258, {wxPickerBase, getTextCtrl, 0}},
+ {2259, {wxPickerBase, isTextCtrlGrowable, 0}},
+ {2260, {wxPickerBase, setPickerCtrlGrowable, 1}},
+ {2261, {wxPickerBase, setTextCtrlGrowable, 1}},
+ {2262, {wxPickerBase, isPickerCtrlGrowable, 0}},
+ {2263, {wxFilePickerCtrl, new_0, 0}},
+ {2264, {wxFilePickerCtrl, new_3, 3}},
+ {2265, {wxFilePickerCtrl, create, 3}},
+ {2266, {wxFilePickerCtrl, getPath, 0}},
+ {2267, {wxFilePickerCtrl, setPath, 1}},
+ {2268, {wxFilePickerCtrl, 'Destroy', undefined}},
+ {2269, {wxDirPickerCtrl, new_0, 0}},
+ {2270, {wxDirPickerCtrl, new_3, 3}},
+ {2271, {wxDirPickerCtrl, create, 3}},
+ {2272, {wxDirPickerCtrl, getPath, 0}},
+ {2273, {wxDirPickerCtrl, setPath, 1}},
+ {2274, {wxDirPickerCtrl, 'Destroy', undefined}},
+ {2275, {wxColourPickerCtrl, new_0, 0}},
+ {2276, {wxColourPickerCtrl, new_3, 3}},
+ {2277, {wxColourPickerCtrl, create, 3}},
+ {2278, {wxColourPickerCtrl, getColour, 0}},
+ {2279, {wxColourPickerCtrl, setColour_1_1, 1}},
+ {2280, {wxColourPickerCtrl, setColour_1_0, 1}},
+ {2281, {wxColourPickerCtrl, 'Destroy', undefined}},
+ {2282, {wxDatePickerCtrl, new_0, 0}},
+ {2283, {wxDatePickerCtrl, new_3, 3}},
+ {2284, {wxDatePickerCtrl, getRange, 2}},
+ {2285, {wxDatePickerCtrl, getValue, 0}},
+ {2286, {wxDatePickerCtrl, setRange, 2}},
+ {2287, {wxDatePickerCtrl, setValue, 1}},
+ {2288, {wxDatePickerCtrl, 'Destroy', undefined}},
+ {2289, {wxFontPickerCtrl, new_0, 0}},
+ {2290, {wxFontPickerCtrl, new_3, 3}},
+ {2291, {wxFontPickerCtrl, create, 3}},
+ {2292, {wxFontPickerCtrl, getSelectedFont, 0}},
+ {2293, {wxFontPickerCtrl, setSelectedFont, 1}},
+ {2294, {wxFontPickerCtrl, getMaxPointSize, 0}},
+ {2295, {wxFontPickerCtrl, setMaxPointSize, 1}},
+ {2296, {wxFontPickerCtrl, 'Destroy', undefined}},
+ {2299, {wxFindReplaceDialog, new_0, 0}},
+ {2300, {wxFindReplaceDialog, new_4, 4}},
+ {2301, {wxFindReplaceDialog, destruct, 0}},
+ {2302, {wxFindReplaceDialog, create, 4}},
+ {2303, {wxFindReplaceDialog, getData, 0}},
+ {2304, {wxFindReplaceData, new_0, 0}},
+ {2305, {wxFindReplaceData, new_1, 1}},
+ {2306, {wxFindReplaceData, getFindString, 0}},
+ {2307, {wxFindReplaceData, getReplaceString, 0}},
+ {2308, {wxFindReplaceData, getFlags, 0}},
+ {2309, {wxFindReplaceData, setFlags, 1}},
+ {2310, {wxFindReplaceData, setFindString, 1}},
+ {2311, {wxFindReplaceData, setReplaceString, 1}},
+ {2312, {wxFindReplaceData, 'Destroy', undefined}},
+ {2313, {wxMultiChoiceDialog, new_0, 0}},
+ {2315, {wxMultiChoiceDialog, new_5, 5}},
+ {2316, {wxMultiChoiceDialog, getSelections, 0}},
+ {2317, {wxMultiChoiceDialog, setSelections, 1}},
+ {2318, {wxMultiChoiceDialog, 'Destroy', undefined}},
+ {2319, {wxSingleChoiceDialog, new_0, 0}},
+ {2321, {wxSingleChoiceDialog, new_5, 5}},
+ {2322, {wxSingleChoiceDialog, getSelection, 0}},
+ {2323, {wxSingleChoiceDialog, getStringSelection, 0}},
+ {2324, {wxSingleChoiceDialog, setSelection, 1}},
+ {2325, {wxSingleChoiceDialog, 'Destroy', undefined}},
+ {2326, {wxTextEntryDialog, new, 3}},
+ {2327, {wxTextEntryDialog, getValue, 0}},
+ {2328, {wxTextEntryDialog, setValue, 1}},
+ {2329, {wxTextEntryDialog, 'Destroy', undefined}},
+ {2330, {wxPasswordEntryDialog, new, 3}},
+ {2331, {wxPasswordEntryDialog, 'Destroy', undefined}},
+ {2332, {wxFontData, new_0, 0}},
+ {2333, {wxFontData, new_1, 1}},
+ {2334, {wxFontData, destruct, 0}},
+ {2335, {wxFontData, enableEffects, 1}},
+ {2336, {wxFontData, getAllowSymbols, 0}},
+ {2337, {wxFontData, getColour, 0}},
+ {2338, {wxFontData, getChosenFont, 0}},
+ {2339, {wxFontData, getEnableEffects, 0}},
+ {2340, {wxFontData, getInitialFont, 0}},
+ {2341, {wxFontData, getShowHelp, 0}},
+ {2342, {wxFontData, setAllowSymbols, 1}},
+ {2343, {wxFontData, setChosenFont, 1}},
+ {2344, {wxFontData, setColour, 1}},
+ {2345, {wxFontData, setInitialFont, 1}},
+ {2346, {wxFontData, setRange, 2}},
+ {2347, {wxFontData, setShowHelp, 1}},
+ {2351, {wxFontDialog, new_0, 0}},
+ {2353, {wxFontDialog, new_2, 2}},
+ {2355, {wxFontDialog, create, 2}},
+ {2356, {wxFontDialog, getFontData, 0}},
+ {2358, {wxFontDialog, 'Destroy', undefined}},
+ {2359, {wxProgressDialog, new, 3}},
+ {2360, {wxProgressDialog, destruct, 0}},
+ {2361, {wxProgressDialog, resume, 0}},
+ {2362, {wxProgressDialog, update_2, 2}},
+ {2363, {wxProgressDialog, update_0, 0}},
+ {2364, {wxMessageDialog, new, 3}},
+ {2365, {wxMessageDialog, destruct, 0}},
+ {2366, {wxPageSetupDialog, new, 2}},
+ {2367, {wxPageSetupDialog, destruct, 0}},
+ {2368, {wxPageSetupDialog, getPageSetupData, 0}},
+ {2369, {wxPageSetupDialog, showModal, 0}},
+ {2370, {wxPageSetupDialogData, new_0, 0}},
+ {2371, {wxPageSetupDialogData, new_1_0, 1}},
+ {2372, {wxPageSetupDialogData, new_1_1, 1}},
+ {2373, {wxPageSetupDialogData, destruct, 0}},
+ {2374, {wxPageSetupDialogData, enableHelp, 1}},
+ {2375, {wxPageSetupDialogData, enableMargins, 1}},
+ {2376, {wxPageSetupDialogData, enableOrientation, 1}},
+ {2377, {wxPageSetupDialogData, enablePaper, 1}},
+ {2378, {wxPageSetupDialogData, enablePrinter, 1}},
+ {2379, {wxPageSetupDialogData, getDefaultMinMargins, 0}},
+ {2380, {wxPageSetupDialogData, getEnableMargins, 0}},
+ {2381, {wxPageSetupDialogData, getEnableOrientation, 0}},
+ {2382, {wxPageSetupDialogData, getEnablePaper, 0}},
+ {2383, {wxPageSetupDialogData, getEnablePrinter, 0}},
+ {2384, {wxPageSetupDialogData, getEnableHelp, 0}},
+ {2385, {wxPageSetupDialogData, getDefaultInfo, 0}},
+ {2386, {wxPageSetupDialogData, getMarginTopLeft, 0}},
+ {2387, {wxPageSetupDialogData, getMarginBottomRight, 0}},
+ {2388, {wxPageSetupDialogData, getMinMarginTopLeft, 0}},
+ {2389, {wxPageSetupDialogData, getMinMarginBottomRight, 0}},
+ {2390, {wxPageSetupDialogData, getPaperId, 0}},
+ {2391, {wxPageSetupDialogData, getPaperSize, 0}},
+ {2393, {wxPageSetupDialogData, getPrintData, 0}},
+ {2394, {wxPageSetupDialogData, isOk, 0}},
+ {2395, {wxPageSetupDialogData, setDefaultInfo, 1}},
+ {2396, {wxPageSetupDialogData, setDefaultMinMargins, 1}},
+ {2397, {wxPageSetupDialogData, setMarginTopLeft, 1}},
+ {2398, {wxPageSetupDialogData, setMarginBottomRight, 1}},
+ {2399, {wxPageSetupDialogData, setMinMarginTopLeft, 1}},
+ {2400, {wxPageSetupDialogData, setMinMarginBottomRight, 1}},
+ {2401, {wxPageSetupDialogData, setPaperId, 1}},
+ {2402, {wxPageSetupDialogData, setPaperSize_1_1, 1}},
+ {2403, {wxPageSetupDialogData, setPaperSize_1_0, 1}},
+ {2404, {wxPageSetupDialogData, setPrintData, 1}},
+ {2405, {wxPrintDialog, new_2_0, 2}},
+ {2406, {wxPrintDialog, new_2_1, 2}},
+ {2407, {wxPrintDialog, destruct, 0}},
+ {2408, {wxPrintDialog, getPrintDialogData, 0}},
+ {2409, {wxPrintDialog, getPrintDC, 0}},
+ {2410, {wxPrintDialogData, new_0, 0}},
+ {2411, {wxPrintDialogData, new_1_1, 1}},
+ {2412, {wxPrintDialogData, new_1_0, 1}},
+ {2413, {wxPrintDialogData, destruct, 0}},
+ {2414, {wxPrintDialogData, enableHelp, 1}},
+ {2415, {wxPrintDialogData, enablePageNumbers, 1}},
+ {2416, {wxPrintDialogData, enablePrintToFile, 1}},
+ {2417, {wxPrintDialogData, enableSelection, 1}},
+ {2418, {wxPrintDialogData, getAllPages, 0}},
+ {2419, {wxPrintDialogData, getCollate, 0}},
+ {2420, {wxPrintDialogData, getFromPage, 0}},
+ {2421, {wxPrintDialogData, getMaxPage, 0}},
+ {2422, {wxPrintDialogData, getMinPage, 0}},
+ {2423, {wxPrintDialogData, getNoCopies, 0}},
+ {2424, {wxPrintDialogData, getPrintData, 0}},
+ {2425, {wxPrintDialogData, getPrintToFile, 0}},
+ {2426, {wxPrintDialogData, getSelection, 0}},
+ {2427, {wxPrintDialogData, getToPage, 0}},
+ {2428, {wxPrintDialogData, isOk, 0}},
+ {2429, {wxPrintDialogData, setCollate, 1}},
+ {2430, {wxPrintDialogData, setFromPage, 1}},
+ {2431, {wxPrintDialogData, setMaxPage, 1}},
+ {2432, {wxPrintDialogData, setMinPage, 1}},
+ {2433, {wxPrintDialogData, setNoCopies, 1}},
+ {2434, {wxPrintDialogData, setPrintData, 1}},
+ {2435, {wxPrintDialogData, setPrintToFile, 1}},
+ {2436, {wxPrintDialogData, setSelection, 1}},
+ {2437, {wxPrintDialogData, setToPage, 1}},
+ {2438, {wxPrintData, new_0, 0}},
+ {2439, {wxPrintData, new_1, 1}},
+ {2440, {wxPrintData, destruct, 0}},
+ {2441, {wxPrintData, getCollate, 0}},
+ {2442, {wxPrintData, getBin, 0}},
+ {2443, {wxPrintData, getColour, 0}},
+ {2444, {wxPrintData, getDuplex, 0}},
+ {2445, {wxPrintData, getNoCopies, 0}},
+ {2446, {wxPrintData, getOrientation, 0}},
+ {2447, {wxPrintData, getPaperId, 0}},
+ {2448, {wxPrintData, getPrinterName, 0}},
+ {2449, {wxPrintData, getQuality, 0}},
+ {2450, {wxPrintData, isOk, 0}},
+ {2451, {wxPrintData, setBin, 1}},
+ {2452, {wxPrintData, setCollate, 1}},
+ {2453, {wxPrintData, setColour, 1}},
+ {2454, {wxPrintData, setDuplex, 1}},
+ {2455, {wxPrintData, setNoCopies, 1}},
+ {2456, {wxPrintData, setOrientation, 1}},
+ {2457, {wxPrintData, setPaperId, 1}},
+ {2458, {wxPrintData, setPrinterName, 1}},
+ {2459, {wxPrintData, setQuality, 1}},
+ {2462, {wxPrintPreview, new_2, 2}},
+ {2463, {wxPrintPreview, new_3, 3}},
+ {2465, {wxPrintPreview, destruct, 0}},
+ {2466, {wxPrintPreview, getCanvas, 0}},
+ {2467, {wxPrintPreview, getCurrentPage, 0}},
+ {2468, {wxPrintPreview, getFrame, 0}},
+ {2469, {wxPrintPreview, getMaxPage, 0}},
+ {2470, {wxPrintPreview, getMinPage, 0}},
+ {2471, {wxPrintPreview, getPrintout, 0}},
+ {2472, {wxPrintPreview, getPrintoutForPrinting, 0}},
+ {2473, {wxPrintPreview, isOk, 0}},
+ {2474, {wxPrintPreview, paintPage, 2}},
+ {2475, {wxPrintPreview, print, 1}},
+ {2476, {wxPrintPreview, renderPage, 1}},
+ {2477, {wxPrintPreview, setCanvas, 1}},
+ {2478, {wxPrintPreview, setCurrentPage, 1}},
+ {2479, {wxPrintPreview, setFrame, 1}},
+ {2480, {wxPrintPreview, setPrintout, 1}},
+ {2481, {wxPrintPreview, setZoom, 1}},
+ {2482, {wxPreviewFrame, new, 3}},
+ {2483, {wxPreviewFrame, destruct, 0}},
+ {2484, {wxPreviewFrame, createControlBar, 0}},
+ {2485, {wxPreviewFrame, createCanvas, 0}},
+ {2486, {wxPreviewFrame, initialize, 0}},
+ {2487, {wxPreviewFrame, onCloseWindow, 1}},
+ {2488, {wxPreviewControlBar, new, 4}},
+ {2489, {wxPreviewControlBar, destruct, 0}},
+ {2490, {wxPreviewControlBar, createButtons, 0}},
+ {2491, {wxPreviewControlBar, getPrintPreview, 0}},
+ {2492, {wxPreviewControlBar, getZoomControl, 0}},
+ {2493, {wxPreviewControlBar, setZoomControl, 1}},
+ {2495, {wxPrinter, new, 1}},
+ {2496, {wxPrinter, createAbortWindow, 2}},
+ {2497, {wxPrinter, getAbort, 0}},
+ {2498, {wxPrinter, getLastError, 0}},
+ {2499, {wxPrinter, getPrintDialogData, 0}},
+ {2500, {wxPrinter, print, 3}},
+ {2501, {wxPrinter, printDialog, 1}},
+ {2502, {wxPrinter, reportError, 3}},
+ {2503, {wxPrinter, setup, 1}},
+ {2504, {wxPrinter, 'Destroy', undefined}},
+ {2505, {wxXmlResource, new_1, 1}},
+ {2506, {wxXmlResource, new_2, 2}},
+ {2507, {wxXmlResource, destruct, 0}},
+ {2508, {wxXmlResource, attachUnknownControl, 3}},
+ {2509, {wxXmlResource, clearHandlers, 0}},
+ {2510, {wxXmlResource, compareVersion, 4}},
+ {2511, {wxXmlResource, get, 0}},
+ {2512, {wxXmlResource, getFlags, 0}},
+ {2513, {wxXmlResource, getVersion, 0}},
+ {2514, {wxXmlResource, getXRCID, 2}},
+ {2515, {wxXmlResource, initAllHandlers, 0}},
+ {2516, {wxXmlResource, load, 1}},
+ {2517, {wxXmlResource, loadBitmap, 1}},
+ {2518, {wxXmlResource, loadDialog_2, 2}},
+ {2519, {wxXmlResource, loadDialog_3, 3}},
+ {2520, {wxXmlResource, loadFrame_2, 2}},
+ {2521, {wxXmlResource, loadFrame_3, 3}},
+ {2522, {wxXmlResource, loadIcon, 1}},
+ {2523, {wxXmlResource, loadMenu, 1}},
+ {2524, {wxXmlResource, loadMenuBar_2, 2}},
+ {2525, {wxXmlResource, loadMenuBar_1, 1}},
+ {2526, {wxXmlResource, loadPanel_2, 2}},
+ {2527, {wxXmlResource, loadPanel_3, 3}},
+ {2528, {wxXmlResource, loadToolBar, 2}},
+ {2529, {wxXmlResource, set, 1}},
+ {2530, {wxXmlResource, setFlags, 1}},
+ {2531, {wxXmlResource, unload, 1}},
+ {2532, {wxXmlResource, xrcctrl, 3}},
+ {2533, {wxHtmlEasyPrinting, new, 1}},
+ {2534, {wxHtmlEasyPrinting, destruct, 0}},
+ {2535, {wxHtmlEasyPrinting, getPrintData, 0}},
+ {2536, {wxHtmlEasyPrinting, getPageSetupData, 0}},
+ {2537, {wxHtmlEasyPrinting, previewFile, 1}},
+ {2538, {wxHtmlEasyPrinting, previewText, 2}},
+ {2539, {wxHtmlEasyPrinting, printFile, 1}},
+ {2540, {wxHtmlEasyPrinting, printText, 2}},
+ {2541, {wxHtmlEasyPrinting, pageSetup, 0}},
+ {2542, {wxHtmlEasyPrinting, setFonts, 3}},
+ {2543, {wxHtmlEasyPrinting, setHeader, 2}},
+ {2544, {wxHtmlEasyPrinting, setFooter, 2}},
+ {2546, {wxGLCanvas, new_2, 2}},
+ {2547, {wxGLCanvas, new_3_1, 3}},
+ {2548, {wxGLCanvas, new_3_0, 3}},
+ {2549, {wxGLCanvas, getContext, 0}},
+ {2551, {wxGLCanvas, setCurrent, 0}},
+ {2552, {wxGLCanvas, swapBuffers, 0}},
+ {2553, {wxGLCanvas, 'Destroy', undefined}},
+ {2554, {wxAuiManager, new, 1}},
+ {2555, {wxAuiManager, destruct, 0}},
+ {2556, {wxAuiManager, addPane_2_1, 2}},
+ {2557, {wxAuiManager, addPane_3, 3}},
+ {2558, {wxAuiManager, addPane_2_0, 2}},
+ {2559, {wxAuiManager, detachPane, 1}},
+ {2560, {wxAuiManager, getAllPanes, 0}},
+ {2561, {wxAuiManager, getArtProvider, 0}},
+ {2562, {wxAuiManager, getDockSizeConstraint, 2}},
+ {2563, {wxAuiManager, getFlags, 0}},
+ {2564, {wxAuiManager, getManagedWindow, 0}},
+ {2565, {wxAuiManager, getManager, 1}},
+ {2566, {wxAuiManager, getPane_1_1, 1}},
+ {2567, {wxAuiManager, getPane_1_0, 1}},
+ {2568, {wxAuiManager, hideHint, 0}},
+ {2569, {wxAuiManager, insertPane, 3}},
+ {2570, {wxAuiManager, loadPaneInfo, 2}},
+ {2571, {wxAuiManager, loadPerspective, 2}},
+ {2572, {wxAuiManager, savePaneInfo, 1}},
+ {2573, {wxAuiManager, savePerspective, 0}},
+ {2574, {wxAuiManager, setArtProvider, 1}},
+ {2575, {wxAuiManager, setDockSizeConstraint, 2}},
+ {2576, {wxAuiManager, setFlags, 1}},
+ {2577, {wxAuiManager, setManagedWindow, 1}},
+ {2578, {wxAuiManager, showHint, 1}},
+ {2579, {wxAuiManager, unInit, 0}},
+ {2580, {wxAuiManager, update, 0}},
+ {2581, {wxAuiPaneInfo, new_0, 0}},
+ {2582, {wxAuiPaneInfo, new_1, 1}},
+ {2583, {wxAuiPaneInfo, destruct, 0}},
+ {2584, {wxAuiPaneInfo, bestSize_1, 1}},
+ {2585, {wxAuiPaneInfo, bestSize_2, 2}},
+ {2586, {wxAuiPaneInfo, bottom, 0}},
+ {2587, {wxAuiPaneInfo, bottomDockable, 1}},
+ {2588, {wxAuiPaneInfo, caption, 1}},
+ {2589, {wxAuiPaneInfo, captionVisible, 1}},
+ {2590, {wxAuiPaneInfo, centre, 0}},
+ {2591, {wxAuiPaneInfo, centrePane, 0}},
+ {2592, {wxAuiPaneInfo, closeButton, 1}},
+ {2593, {wxAuiPaneInfo, defaultPane, 0}},
+ {2594, {wxAuiPaneInfo, destroyOnClose, 1}},
+ {2595, {wxAuiPaneInfo, direction, 1}},
+ {2596, {wxAuiPaneInfo, dock, 0}},
+ {2597, {wxAuiPaneInfo, dockable, 1}},
+ {2598, {wxAuiPaneInfo, fixed, 0}},
+ {2599, {wxAuiPaneInfo, float, 0}},
+ {2600, {wxAuiPaneInfo, floatable, 1}},
+ {2601, {wxAuiPaneInfo, floatingPosition_1, 1}},
+ {2602, {wxAuiPaneInfo, floatingPosition_2, 2}},
+ {2603, {wxAuiPaneInfo, floatingSize_1, 1}},
+ {2604, {wxAuiPaneInfo, floatingSize_2, 2}},
+ {2605, {wxAuiPaneInfo, gripper, 1}},
+ {2606, {wxAuiPaneInfo, gripperTop, 1}},
+ {2607, {wxAuiPaneInfo, hasBorder, 0}},
+ {2608, {wxAuiPaneInfo, hasCaption, 0}},
+ {2609, {wxAuiPaneInfo, hasCloseButton, 0}},
+ {2610, {wxAuiPaneInfo, hasFlag, 1}},
+ {2611, {wxAuiPaneInfo, hasGripper, 0}},
+ {2612, {wxAuiPaneInfo, hasGripperTop, 0}},
+ {2613, {wxAuiPaneInfo, hasMaximizeButton, 0}},
+ {2614, {wxAuiPaneInfo, hasMinimizeButton, 0}},
+ {2615, {wxAuiPaneInfo, hasPinButton, 0}},
+ {2616, {wxAuiPaneInfo, hide, 0}},
+ {2617, {wxAuiPaneInfo, isBottomDockable, 0}},
+ {2618, {wxAuiPaneInfo, isDocked, 0}},
+ {2619, {wxAuiPaneInfo, isFixed, 0}},
+ {2620, {wxAuiPaneInfo, isFloatable, 0}},
+ {2621, {wxAuiPaneInfo, isFloating, 0}},
+ {2622, {wxAuiPaneInfo, isLeftDockable, 0}},
+ {2623, {wxAuiPaneInfo, isMovable, 0}},
+ {2624, {wxAuiPaneInfo, isOk, 0}},
+ {2625, {wxAuiPaneInfo, isResizable, 0}},
+ {2626, {wxAuiPaneInfo, isRightDockable, 0}},
+ {2627, {wxAuiPaneInfo, isShown, 0}},
+ {2628, {wxAuiPaneInfo, isToolbar, 0}},
+ {2629, {wxAuiPaneInfo, isTopDockable, 0}},
+ {2630, {wxAuiPaneInfo, layer, 1}},
+ {2631, {wxAuiPaneInfo, left, 0}},
+ {2632, {wxAuiPaneInfo, leftDockable, 1}},
+ {2633, {wxAuiPaneInfo, maxSize_1, 1}},
+ {2634, {wxAuiPaneInfo, maxSize_2, 2}},
+ {2635, {wxAuiPaneInfo, maximizeButton, 1}},
+ {2636, {wxAuiPaneInfo, minSize_1, 1}},
+ {2637, {wxAuiPaneInfo, minSize_2, 2}},
+ {2638, {wxAuiPaneInfo, minimizeButton, 1}},
+ {2639, {wxAuiPaneInfo, movable, 1}},
+ {2640, {wxAuiPaneInfo, name, 1}},
+ {2641, {wxAuiPaneInfo, paneBorder, 1}},
+ {2642, {wxAuiPaneInfo, pinButton, 1}},
+ {2643, {wxAuiPaneInfo, position, 1}},
+ {2644, {wxAuiPaneInfo, resizable, 1}},
+ {2645, {wxAuiPaneInfo, right, 0}},
+ {2646, {wxAuiPaneInfo, rightDockable, 1}},
+ {2647, {wxAuiPaneInfo, row, 1}},
+ {2648, {wxAuiPaneInfo, safeSet, 1}},
+ {2649, {wxAuiPaneInfo, setFlag, 2}},
+ {2650, {wxAuiPaneInfo, show, 1}},
+ {2651, {wxAuiPaneInfo, toolbarPane, 0}},
+ {2652, {wxAuiPaneInfo, top, 0}},
+ {2653, {wxAuiPaneInfo, topDockable, 1}},
+ {2654, {wxAuiPaneInfo, window, 1}},
+ {2655, {wxAuiPaneInfo, getWindow, 0}},
+ {2656, {wxAuiPaneInfo, getFrame, 0}},
+ {2657, {wxAuiPaneInfo, getDirection, 0}},
+ {2658, {wxAuiPaneInfo, getLayer, 0}},
+ {2659, {wxAuiPaneInfo, getRow, 0}},
+ {2660, {wxAuiPaneInfo, getPosition, 0}},
+ {2661, {wxAuiPaneInfo, getFloatingPosition, 0}},
+ {2662, {wxAuiPaneInfo, getFloatingSize, 0}},
+ {2663, {wxAuiNotebook, new_0, 0}},
+ {2664, {wxAuiNotebook, new_2, 2}},
+ {2665, {wxAuiNotebook, addPage, 3}},
+ {2666, {wxAuiNotebook, create, 2}},
+ {2667, {wxAuiNotebook, deletePage, 1}},
+ {2668, {wxAuiNotebook, getArtProvider, 0}},
+ {2669, {wxAuiNotebook, getPage, 1}},
+ {2670, {wxAuiNotebook, getPageBitmap, 1}},
+ {2671, {wxAuiNotebook, getPageCount, 0}},
+ {2672, {wxAuiNotebook, getPageIndex, 1}},
+ {2673, {wxAuiNotebook, getPageText, 1}},
+ {2674, {wxAuiNotebook, getSelection, 0}},
+ {2675, {wxAuiNotebook, insertPage, 4}},
+ {2676, {wxAuiNotebook, removePage, 1}},
+ {2677, {wxAuiNotebook, setArtProvider, 1}},
+ {2678, {wxAuiNotebook, setFont, 1}},
+ {2679, {wxAuiNotebook, setPageBitmap, 2}},
+ {2680, {wxAuiNotebook, setPageText, 2}},
+ {2681, {wxAuiNotebook, setSelection, 1}},
+ {2682, {wxAuiNotebook, setTabCtrlHeight, 1}},
+ {2683, {wxAuiNotebook, setUniformBitmapSize, 1}},
+ {2684, {wxAuiNotebook, 'Destroy', undefined}},
+ {2685, {wxAuiTabArt, setFlags, 1}},
+ {2686, {wxAuiTabArt, setMeasuringFont, 1}},
+ {2687, {wxAuiTabArt, setNormalFont, 1}},
+ {2688, {wxAuiTabArt, setSelectedFont, 1}},
+ {2689, {wxAuiTabArt, setColour, 1}},
+ {2690, {wxAuiTabArt, setActiveColour, 1}},
+ {2691, {wxAuiDockArt, getColour, 1}},
+ {2692, {wxAuiDockArt, getFont, 1}},
+ {2693, {wxAuiDockArt, getMetric, 1}},
+ {2694, {wxAuiDockArt, setColour, 2}},
+ {2695, {wxAuiDockArt, setFont, 2}},
+ {2696, {wxAuiDockArt, setMetric, 2}},
+ {2697, {wxAuiSimpleTabArt, new, 0}},
+ {2698, {wxAuiSimpleTabArt, 'Destroy', undefined}},
+ {2699, {wxMDIParentFrame, new_0, 0}},
+ {2700, {wxMDIParentFrame, new_4, 4}},
+ {2701, {wxMDIParentFrame, destruct, 0}},
+ {2702, {wxMDIParentFrame, activateNext, 0}},
+ {2703, {wxMDIParentFrame, activatePrevious, 0}},
+ {2704, {wxMDIParentFrame, arrangeIcons, 0}},
+ {2705, {wxMDIParentFrame, cascade, 0}},
+ {2706, {wxMDIParentFrame, create, 4}},
+ {2707, {wxMDIParentFrame, getActiveChild, 0}},
+ {2708, {wxMDIParentFrame, getClientWindow, 0}},
+ {2709, {wxMDIParentFrame, tile, 1}},
+ {2710, {wxMDIChildFrame, new_0, 0}},
+ {2711, {wxMDIChildFrame, new_4, 4}},
+ {2712, {wxMDIChildFrame, destruct, 0}},
+ {2713, {wxMDIChildFrame, activate, 0}},
+ {2714, {wxMDIChildFrame, create, 4}},
+ {2715, {wxMDIChildFrame, maximize, 1}},
+ {2716, {wxMDIChildFrame, restore, 0}},
+ {2717, {wxMDIClientWindow, new_0, 0}},
+ {2718, {wxMDIClientWindow, new_2, 2}},
+ {2719, {wxMDIClientWindow, destruct, 0}},
+ {2720, {wxMDIClientWindow, createClient, 2}},
+ {2721, {wxLayoutAlgorithm, new, 0}},
+ {2722, {wxLayoutAlgorithm, layoutFrame, 2}},
+ {2723, {wxLayoutAlgorithm, layoutMDIFrame, 2}},
+ {2724, {wxLayoutAlgorithm, layoutWindow, 2}},
+ {2725, {wxLayoutAlgorithm, 'Destroy', undefined}},
+ {2726, {wxEvent, getId, 0}},
+ {2727, {wxEvent, getSkipped, 0}},
+ {2728, {wxEvent, getTimestamp, 0}},
+ {2729, {wxEvent, isCommandEvent, 0}},
+ {2730, {wxEvent, resumePropagation, 1}},
+ {2731, {wxEvent, shouldPropagate, 0}},
+ {2732, {wxEvent, skip, 1}},
+ {2733, {wxEvent, stopPropagation, 0}},
+ {2734, {wxCommandEvent, getClientData, 0}},
+ {2735, {wxCommandEvent, getExtraLong, 0}},
+ {2736, {wxCommandEvent, getInt, 0}},
+ {2737, {wxCommandEvent, getSelection, 0}},
+ {2738, {wxCommandEvent, getString, 0}},
+ {2739, {wxCommandEvent, isChecked, 0}},
+ {2740, {wxCommandEvent, isSelection, 0}},
+ {2741, {wxCommandEvent, setInt, 1}},
+ {2742, {wxCommandEvent, setString, 1}},
+ {2743, {wxScrollEvent, getOrientation, 0}},
+ {2744, {wxScrollEvent, getPosition, 0}},
+ {2745, {wxScrollWinEvent, getOrientation, 0}},
+ {2746, {wxScrollWinEvent, getPosition, 0}},
+ {2747, {wxMouseEvent, altDown, 0}},
+ {2748, {wxMouseEvent, button, 1}},
+ {2749, {wxMouseEvent, buttonDClick, 1}},
+ {2750, {wxMouseEvent, buttonDown, 1}},
+ {2751, {wxMouseEvent, buttonUp, 1}},
+ {2752, {wxMouseEvent, cmdDown, 0}},
+ {2753, {wxMouseEvent, controlDown, 0}},
+ {2754, {wxMouseEvent, dragging, 0}},
+ {2755, {wxMouseEvent, entering, 0}},
+ {2756, {wxMouseEvent, getButton, 0}},
+ {2759, {wxMouseEvent, getPosition, 0}},
+ {2760, {wxMouseEvent, getLogicalPosition, 1}},
+ {2761, {wxMouseEvent, getLinesPerAction, 0}},
+ {2762, {wxMouseEvent, getWheelRotation, 0}},
+ {2763, {wxMouseEvent, getWheelDelta, 0}},
+ {2764, {wxMouseEvent, getX, 0}},
+ {2765, {wxMouseEvent, getY, 0}},
+ {2766, {wxMouseEvent, isButton, 0}},
+ {2767, {wxMouseEvent, isPageScroll, 0}},
+ {2768, {wxMouseEvent, leaving, 0}},
+ {2769, {wxMouseEvent, leftDClick, 0}},
+ {2770, {wxMouseEvent, leftDown, 0}},
+ {2771, {wxMouseEvent, leftIsDown, 0}},
+ {2772, {wxMouseEvent, leftUp, 0}},
+ {2773, {wxMouseEvent, metaDown, 0}},
+ {2774, {wxMouseEvent, middleDClick, 0}},
+ {2775, {wxMouseEvent, middleDown, 0}},
+ {2776, {wxMouseEvent, middleIsDown, 0}},
+ {2777, {wxMouseEvent, middleUp, 0}},
+ {2778, {wxMouseEvent, moving, 0}},
+ {2779, {wxMouseEvent, rightDClick, 0}},
+ {2780, {wxMouseEvent, rightDown, 0}},
+ {2781, {wxMouseEvent, rightIsDown, 0}},
+ {2782, {wxMouseEvent, rightUp, 0}},
+ {2783, {wxMouseEvent, shiftDown, 0}},
+ {2784, {wxSetCursorEvent, getCursor, 0}},
+ {2785, {wxSetCursorEvent, getX, 0}},
+ {2786, {wxSetCursorEvent, getY, 0}},
+ {2787, {wxSetCursorEvent, hasCursor, 0}},
+ {2788, {wxSetCursorEvent, setCursor, 1}},
+ {2789, {wxKeyEvent, altDown, 0}},
+ {2790, {wxKeyEvent, cmdDown, 0}},
+ {2791, {wxKeyEvent, controlDown, 0}},
+ {2792, {wxKeyEvent, getKeyCode, 0}},
+ {2793, {wxKeyEvent, getModifiers, 0}},
+ {2796, {wxKeyEvent, getPosition, 0}},
+ {2797, {wxKeyEvent, getRawKeyCode, 0}},
+ {2798, {wxKeyEvent, getRawKeyFlags, 0}},
+ {2799, {wxKeyEvent, getUnicodeKey, 0}},
+ {2800, {wxKeyEvent, getX, 0}},
+ {2801, {wxKeyEvent, getY, 0}},
+ {2802, {wxKeyEvent, hasModifiers, 0}},
+ {2803, {wxKeyEvent, metaDown, 0}},
+ {2804, {wxKeyEvent, shiftDown, 0}},
+ {2805, {wxSizeEvent, getSize, 0}},
+ {2806, {wxMoveEvent, getPosition, 0}},
+ {2807, {wxEraseEvent, getDC, 0}},
+ {2808, {wxFocusEvent, getWindow, 0}},
+ {2809, {wxChildFocusEvent, getWindow, 0}},
+ {2810, {wxMenuEvent, getMenu, 0}},
+ {2811, {wxMenuEvent, getMenuId, 0}},
+ {2812, {wxMenuEvent, isPopup, 0}},
+ {2813, {wxCloseEvent, canVeto, 0}},
+ {2814, {wxCloseEvent, getLoggingOff, 0}},
+ {2815, {wxCloseEvent, setCanVeto, 1}},
+ {2816, {wxCloseEvent, setLoggingOff, 1}},
+ {2817, {wxCloseEvent, veto, 1}},
+ {2818, {wxShowEvent, setShow, 1}},
+ {2819, {wxShowEvent, getShow, 0}},
+ {2820, {wxIconizeEvent, iconized, 0}},
+ {2821, {wxJoystickEvent, buttonDown, 1}},
+ {2822, {wxJoystickEvent, buttonIsDown, 1}},
+ {2823, {wxJoystickEvent, buttonUp, 1}},
+ {2824, {wxJoystickEvent, getButtonChange, 0}},
+ {2825, {wxJoystickEvent, getButtonState, 0}},
+ {2826, {wxJoystickEvent, getJoystick, 0}},
+ {2827, {wxJoystickEvent, getPosition, 0}},
+ {2828, {wxJoystickEvent, getZPosition, 0}},
+ {2829, {wxJoystickEvent, isButton, 0}},
+ {2830, {wxJoystickEvent, isMove, 0}},
+ {2831, {wxJoystickEvent, isZMove, 0}},
+ {2832, {wxUpdateUIEvent, canUpdate, 1}},
+ {2833, {wxUpdateUIEvent, check, 1}},
+ {2834, {wxUpdateUIEvent, enable, 1}},
+ {2835, {wxUpdateUIEvent, show, 1}},
+ {2836, {wxUpdateUIEvent, getChecked, 0}},
+ {2837, {wxUpdateUIEvent, getEnabled, 0}},
+ {2838, {wxUpdateUIEvent, getShown, 0}},
+ {2839, {wxUpdateUIEvent, getSetChecked, 0}},
+ {2840, {wxUpdateUIEvent, getSetEnabled, 0}},
+ {2841, {wxUpdateUIEvent, getSetShown, 0}},
+ {2842, {wxUpdateUIEvent, getSetText, 0}},
+ {2843, {wxUpdateUIEvent, getText, 0}},
+ {2844, {wxUpdateUIEvent, getMode, 0}},
+ {2845, {wxUpdateUIEvent, getUpdateInterval, 0}},
+ {2846, {wxUpdateUIEvent, resetUpdateTime, 0}},
+ {2847, {wxUpdateUIEvent, setMode, 1}},
+ {2848, {wxUpdateUIEvent, setText, 1}},
+ {2849, {wxUpdateUIEvent, setUpdateInterval, 1}},
+ {2850, {wxMouseCaptureChangedEvent, getCapturedWindow, 0}},
+ {2851, {wxPaletteChangedEvent, setChangedWindow, 1}},
+ {2852, {wxPaletteChangedEvent, getChangedWindow, 0}},
+ {2853, {wxQueryNewPaletteEvent, setPaletteRealized, 1}},
+ {2854, {wxQueryNewPaletteEvent, getPaletteRealized, 0}},
+ {2855, {wxNavigationKeyEvent, getDirection, 0}},
+ {2856, {wxNavigationKeyEvent, setDirection, 1}},
+ {2857, {wxNavigationKeyEvent, isWindowChange, 0}},
+ {2858, {wxNavigationKeyEvent, setWindowChange, 1}},
+ {2859, {wxNavigationKeyEvent, isFromTab, 0}},
+ {2860, {wxNavigationKeyEvent, setFromTab, 1}},
+ {2861, {wxNavigationKeyEvent, getCurrentFocus, 0}},
+ {2862, {wxNavigationKeyEvent, setCurrentFocus, 1}},
+ {2863, {wxHelpEvent, getOrigin, 0}},
+ {2864, {wxHelpEvent, getPosition, 0}},
+ {2865, {wxHelpEvent, setOrigin, 1}},
+ {2866, {wxHelpEvent, setPosition, 1}},
+ {2867, {wxContextMenuEvent, getPosition, 0}},
+ {2868, {wxContextMenuEvent, setPosition, 1}},
+ {2869, {wxIdleEvent, canSend, 1}},
+ {2870, {wxIdleEvent, getMode, 0}},
+ {2871, {wxIdleEvent, requestMore, 1}},
+ {2872, {wxIdleEvent, moreRequested, 0}},
+ {2873, {wxIdleEvent, setMode, 1}},
+ {2874, {wxGridEvent, altDown, 0}},
+ {2875, {wxGridEvent, controlDown, 0}},
+ {2876, {wxGridEvent, getCol, 0}},
+ {2877, {wxGridEvent, getPosition, 0}},
+ {2878, {wxGridEvent, getRow, 0}},
+ {2879, {wxGridEvent, metaDown, 0}},
+ {2880, {wxGridEvent, selecting, 0}},
+ {2881, {wxGridEvent, shiftDown, 0}},
+ {2882, {wxNotifyEvent, allow, 0}},
+ {2883, {wxNotifyEvent, isAllowed, 0}},
+ {2884, {wxNotifyEvent, veto, 0}},
+ {2885, {wxSashEvent, getEdge, 0}},
+ {2886, {wxSashEvent, getDragRect, 0}},
+ {2887, {wxSashEvent, getDragStatus, 0}},
+ {2888, {wxListEvent, getCacheFrom, 0}},
+ {2889, {wxListEvent, getCacheTo, 0}},
+ {2890, {wxListEvent, getKeyCode, 0}},
+ {2891, {wxListEvent, getIndex, 0}},
+ {2892, {wxListEvent, getColumn, 0}},
+ {2893, {wxListEvent, getPoint, 0}},
+ {2894, {wxListEvent, getLabel, 0}},
+ {2895, {wxListEvent, getText, 0}},
+ {2896, {wxListEvent, getImage, 0}},
+ {2897, {wxListEvent, getData, 0}},
+ {2898, {wxListEvent, getMask, 0}},
+ {2899, {wxListEvent, getItem, 0}},
+ {2900, {wxListEvent, isEditCancelled, 0}},
+ {2901, {wxDateEvent, getDate, 0}},
+ {2902, {wxCalendarEvent, getWeekDay, 0}},
+ {2903, {wxFileDirPickerEvent, getPath, 0}},
+ {2904, {wxColourPickerEvent, getColour, 0}},
+ {2905, {wxFontPickerEvent, getFont, 0}},
+ {2906, {wxStyledTextEvent, getPosition, 0}},
+ {2907, {wxStyledTextEvent, getKey, 0}},
+ {2908, {wxStyledTextEvent, getModifiers, 0}},
+ {2909, {wxStyledTextEvent, getModificationType, 0}},
+ {2910, {wxStyledTextEvent, getText, 0}},
+ {2911, {wxStyledTextEvent, getLength, 0}},
+ {2912, {wxStyledTextEvent, getLinesAdded, 0}},
+ {2913, {wxStyledTextEvent, getLine, 0}},
+ {2914, {wxStyledTextEvent, getFoldLevelNow, 0}},
+ {2915, {wxStyledTextEvent, getFoldLevelPrev, 0}},
+ {2916, {wxStyledTextEvent, getMargin, 0}},
+ {2917, {wxStyledTextEvent, getMessage, 0}},
+ {2918, {wxStyledTextEvent, getWParam, 0}},
+ {2919, {wxStyledTextEvent, getLParam, 0}},
+ {2920, {wxStyledTextEvent, getListType, 0}},
+ {2921, {wxStyledTextEvent, getX, 0}},
+ {2922, {wxStyledTextEvent, getY, 0}},
+ {2923, {wxStyledTextEvent, getDragText, 0}},
+ {2924, {wxStyledTextEvent, getDragAllowMove, 0}},
+ {2925, {wxStyledTextEvent, getDragResult, 0}},
+ {2926, {wxStyledTextEvent, getShift, 0}},
+ {2927, {wxStyledTextEvent, getControl, 0}},
+ {2928, {wxStyledTextEvent, getAlt, 0}},
+ {2929, {utils, getKeyState, 1}},
+ {2930, {utils, getMousePosition, 2}},
+ {2931, {utils, getMouseState, 0}},
+ {2932, {utils, setDetectableAutoRepeat, 1}},
+ {2933, {utils, bell, 0}},
+ {2934, {utils, findMenuItemId, 3}},
+ {2935, {utils, genericFindWindowAtPoint, 1}},
+ {2936, {utils, findWindowAtPoint, 1}},
+ {2937, {utils, beginBusyCursor, 1}},
+ {2938, {utils, endBusyCursor, 0}},
+ {2939, {utils, isBusy, 0}},
+ {2940, {utils, shutdown, 1}},
+ {2941, {utils, shell, 1}},
+ {2942, {utils, launchDefaultBrowser, 2}},
+ {2943, {utils, getEmailAddress, 0}},
+ {2944, {utils, getUserId, 0}},
+ {2945, {utils, getHomeDir, 0}},
+ {2946, {utils, newId, 0}},
+ {2947, {utils, registerId, 1}},
+ {2948, {utils, getCurrentId, 0}},
+ {2949, {utils, getOsDescription, 0}},
+ {2950, {utils, isPlatformLittleEndian, 0}},
+ {2951, {utils, isPlatform64Bit, 0}},
+ {2952, {gdicmn, displaySize, 2}},
+ {2953, {gdicmn, setCursor, 1}},
+ {2954, {wxPrintout, new, 1}},
+ {2955, {wxPrintout, destruct, 0}},
+ {2956, {wxPrintout, getDC, 0}},
+ {2957, {wxPrintout, getPageSizeMM, 2}},
+ {2958, {wxPrintout, getPageSizePixels, 2}},
+ {2959, {wxPrintout, getPaperRectPixels, 0}},
+ {2960, {wxPrintout, getPPIPrinter, 2}},
+ {2961, {wxPrintout, getPPIScreen, 2}},
+ {2962, {wxPrintout, getTitle, 0}},
+ {2963, {wxPrintout, isPreview, 0}},
+ {2964, {wxPrintout, fitThisSizeToPaper, 1}},
+ {2965, {wxPrintout, fitThisSizeToPage, 1}},
+ {2966, {wxPrintout, fitThisSizeToPageMargins, 2}},
+ {2967, {wxPrintout, mapScreenSizeToPaper, 0}},
+ {2968, {wxPrintout, mapScreenSizeToPage, 0}},
+ {2969, {wxPrintout, mapScreenSizeToPageMargins, 1}},
+ {2970, {wxPrintout, mapScreenSizeToDevice, 0}},
+ {2971, {wxPrintout, getLogicalPaperRect, 0}},
+ {2972, {wxPrintout, getLogicalPageRect, 0}},
+ {2973, {wxPrintout, getLogicalPageMarginsRect, 1}},
+ {2974, {wxPrintout, setLogicalOrigin, 2}},
+ {2975, {wxPrintout, offsetLogicalOrigin, 2}},
+ {2976, {wxStyledTextCtrl, new_2, 2}},
+ {2977, {wxStyledTextCtrl, new_0, 0}},
+ {2978, {wxStyledTextCtrl, destruct, 0}},
+ {2979, {wxStyledTextCtrl, create, 2}},
+ {2980, {wxStyledTextCtrl, addText, 1}},
+ {2981, {wxStyledTextCtrl, addStyledText, 1}},
+ {2982, {wxStyledTextCtrl, insertText, 2}},
+ {2983, {wxStyledTextCtrl, clearAll, 0}},
+ {2984, {wxStyledTextCtrl, clearDocumentStyle, 0}},
+ {2985, {wxStyledTextCtrl, getLength, 0}},
+ {2986, {wxStyledTextCtrl, getCharAt, 1}},
+ {2987, {wxStyledTextCtrl, getCurrentPos, 0}},
+ {2988, {wxStyledTextCtrl, getAnchor, 0}},
+ {2989, {wxStyledTextCtrl, getStyleAt, 1}},
+ {2990, {wxStyledTextCtrl, redo, 0}},
+ {2991, {wxStyledTextCtrl, setUndoCollection, 1}},
+ {2992, {wxStyledTextCtrl, selectAll, 0}},
+ {2993, {wxStyledTextCtrl, setSavePoint, 0}},
+ {2994, {wxStyledTextCtrl, getStyledText, 2}},
+ {2995, {wxStyledTextCtrl, canRedo, 0}},
+ {2996, {wxStyledTextCtrl, markerLineFromHandle, 1}},
+ {2997, {wxStyledTextCtrl, markerDeleteHandle, 1}},
+ {2998, {wxStyledTextCtrl, getUndoCollection, 0}},
+ {2999, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
+ {3000, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
+ {3001, {wxStyledTextCtrl, positionFromPoint, 1}},
+ {3002, {wxStyledTextCtrl, positionFromPointClose, 2}},
+ {3003, {wxStyledTextCtrl, gotoLine, 1}},
+ {3004, {wxStyledTextCtrl, gotoPos, 1}},
+ {3005, {wxStyledTextCtrl, setAnchor, 1}},
+ {3006, {wxStyledTextCtrl, getCurLine, 1}},
+ {3007, {wxStyledTextCtrl, getEndStyled, 0}},
+ {3008, {wxStyledTextCtrl, convertEOLs, 1}},
+ {3009, {wxStyledTextCtrl, getEOLMode, 0}},
+ {3010, {wxStyledTextCtrl, setEOLMode, 1}},
+ {3011, {wxStyledTextCtrl, startStyling, 2}},
+ {3012, {wxStyledTextCtrl, setStyling, 2}},
+ {3013, {wxStyledTextCtrl, getBufferedDraw, 0}},
+ {3014, {wxStyledTextCtrl, setBufferedDraw, 1}},
+ {3015, {wxStyledTextCtrl, setTabWidth, 1}},
+ {3016, {wxStyledTextCtrl, getTabWidth, 0}},
+ {3017, {wxStyledTextCtrl, setCodePage, 1}},
+ {3018, {wxStyledTextCtrl, markerDefine, 3}},
+ {3019, {wxStyledTextCtrl, markerSetForeground, 2}},
+ {3020, {wxStyledTextCtrl, markerSetBackground, 2}},
+ {3021, {wxStyledTextCtrl, markerAdd, 2}},
+ {3022, {wxStyledTextCtrl, markerDelete, 2}},
+ {3023, {wxStyledTextCtrl, markerDeleteAll, 1}},
+ {3024, {wxStyledTextCtrl, markerGet, 1}},
+ {3025, {wxStyledTextCtrl, markerNext, 2}},
+ {3026, {wxStyledTextCtrl, markerPrevious, 2}},
+ {3027, {wxStyledTextCtrl, markerDefineBitmap, 2}},
+ {3028, {wxStyledTextCtrl, markerAddSet, 2}},
+ {3029, {wxStyledTextCtrl, markerSetAlpha, 2}},
+ {3030, {wxStyledTextCtrl, setMarginType, 2}},
+ {3031, {wxStyledTextCtrl, getMarginType, 1}},
+ {3032, {wxStyledTextCtrl, setMarginWidth, 2}},
+ {3033, {wxStyledTextCtrl, getMarginWidth, 1}},
+ {3034, {wxStyledTextCtrl, setMarginMask, 2}},
+ {3035, {wxStyledTextCtrl, getMarginMask, 1}},
+ {3036, {wxStyledTextCtrl, setMarginSensitive, 2}},
+ {3037, {wxStyledTextCtrl, getMarginSensitive, 1}},
+ {3038, {wxStyledTextCtrl, styleClearAll, 0}},
+ {3039, {wxStyledTextCtrl, styleSetForeground, 2}},
+ {3040, {wxStyledTextCtrl, styleSetBackground, 2}},
+ {3041, {wxStyledTextCtrl, styleSetBold, 2}},
+ {3042, {wxStyledTextCtrl, styleSetItalic, 2}},
+ {3043, {wxStyledTextCtrl, styleSetSize, 2}},
+ {3044, {wxStyledTextCtrl, styleSetFaceName, 2}},
+ {3045, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
+ {3046, {wxStyledTextCtrl, styleResetDefault, 0}},
+ {3047, {wxStyledTextCtrl, styleSetUnderline, 2}},
+ {3048, {wxStyledTextCtrl, styleSetCase, 2}},
+ {3049, {wxStyledTextCtrl, styleSetHotSpot, 2}},
+ {3050, {wxStyledTextCtrl, setSelForeground, 2}},
+ {3051, {wxStyledTextCtrl, setSelBackground, 2}},
+ {3052, {wxStyledTextCtrl, getSelAlpha, 0}},
+ {3053, {wxStyledTextCtrl, setSelAlpha, 1}},
+ {3054, {wxStyledTextCtrl, setCaretForeground, 1}},
+ {3055, {wxStyledTextCtrl, cmdKeyAssign, 3}},
+ {3056, {wxStyledTextCtrl, cmdKeyClear, 2}},
+ {3057, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
+ {3058, {wxStyledTextCtrl, setStyleBytes, 2}},
+ {3059, {wxStyledTextCtrl, styleSetVisible, 2}},
+ {3060, {wxStyledTextCtrl, getCaretPeriod, 0}},
+ {3061, {wxStyledTextCtrl, setCaretPeriod, 1}},
+ {3062, {wxStyledTextCtrl, setWordChars, 1}},
+ {3063, {wxStyledTextCtrl, beginUndoAction, 0}},
+ {3064, {wxStyledTextCtrl, endUndoAction, 0}},
+ {3065, {wxStyledTextCtrl, indicatorSetStyle, 2}},
+ {3066, {wxStyledTextCtrl, indicatorGetStyle, 1}},
+ {3067, {wxStyledTextCtrl, indicatorSetForeground, 2}},
+ {3068, {wxStyledTextCtrl, indicatorGetForeground, 1}},
+ {3069, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
+ {3070, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
+ {3071, {wxStyledTextCtrl, getStyleBits, 0}},
+ {3072, {wxStyledTextCtrl, setLineState, 2}},
+ {3073, {wxStyledTextCtrl, getLineState, 1}},
+ {3074, {wxStyledTextCtrl, getMaxLineState, 0}},
+ {3075, {wxStyledTextCtrl, getCaretLineVisible, 0}},
+ {3076, {wxStyledTextCtrl, setCaretLineVisible, 1}},
+ {3077, {wxStyledTextCtrl, getCaretLineBackground, 0}},
+ {3078, {wxStyledTextCtrl, setCaretLineBackground, 1}},
+ {3079, {wxStyledTextCtrl, autoCompShow, 2}},
+ {3080, {wxStyledTextCtrl, autoCompCancel, 0}},
+ {3081, {wxStyledTextCtrl, autoCompActive, 0}},
+ {3082, {wxStyledTextCtrl, autoCompPosStart, 0}},
+ {3083, {wxStyledTextCtrl, autoCompComplete, 0}},
+ {3084, {wxStyledTextCtrl, autoCompStops, 1}},
+ {3085, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
+ {3086, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
+ {3087, {wxStyledTextCtrl, autoCompSelect, 1}},
+ {3088, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
+ {3089, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
+ {3090, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
+ {3091, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
+ {3092, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
+ {3093, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
+ {3094, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
+ {3095, {wxStyledTextCtrl, userListShow, 2}},
+ {3096, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
+ {3097, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
+ {3098, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
+ {3099, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
+ {3100, {wxStyledTextCtrl, registerImage, 2}},
+ {3101, {wxStyledTextCtrl, clearRegisteredImages, 0}},
+ {3102, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
+ {3103, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
+ {3104, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
+ {3105, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
+ {3106, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
+ {3107, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
+ {3108, {wxStyledTextCtrl, setIndent, 1}},
+ {3109, {wxStyledTextCtrl, getIndent, 0}},
+ {3110, {wxStyledTextCtrl, setUseTabs, 1}},
+ {3111, {wxStyledTextCtrl, getUseTabs, 0}},
+ {3112, {wxStyledTextCtrl, setLineIndentation, 2}},
+ {3113, {wxStyledTextCtrl, getLineIndentation, 1}},
+ {3114, {wxStyledTextCtrl, getLineIndentPosition, 1}},
+ {3115, {wxStyledTextCtrl, getColumn, 1}},
+ {3116, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
+ {3117, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
+ {3118, {wxStyledTextCtrl, setIndentationGuides, 1}},
+ {3119, {wxStyledTextCtrl, getIndentationGuides, 0}},
+ {3120, {wxStyledTextCtrl, setHighlightGuide, 1}},
+ {3121, {wxStyledTextCtrl, getHighlightGuide, 0}},
+ {3122, {wxStyledTextCtrl, getLineEndPosition, 1}},
+ {3123, {wxStyledTextCtrl, getCodePage, 0}},
+ {3124, {wxStyledTextCtrl, getCaretForeground, 0}},
+ {3125, {wxStyledTextCtrl, getReadOnly, 0}},
+ {3126, {wxStyledTextCtrl, setCurrentPos, 1}},
+ {3127, {wxStyledTextCtrl, setSelectionStart, 1}},
+ {3128, {wxStyledTextCtrl, getSelectionStart, 0}},
+ {3129, {wxStyledTextCtrl, setSelectionEnd, 1}},
+ {3130, {wxStyledTextCtrl, getSelectionEnd, 0}},
+ {3131, {wxStyledTextCtrl, setPrintMagnification, 1}},
+ {3132, {wxStyledTextCtrl, getPrintMagnification, 0}},
+ {3133, {wxStyledTextCtrl, setPrintColourMode, 1}},
+ {3134, {wxStyledTextCtrl, getPrintColourMode, 0}},
+ {3135, {wxStyledTextCtrl, findText, 4}},
+ {3136, {wxStyledTextCtrl, formatRange, 7}},
+ {3137, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
+ {3138, {wxStyledTextCtrl, getLine, 1}},
+ {3139, {wxStyledTextCtrl, getLineCount, 0}},
+ {3140, {wxStyledTextCtrl, setMarginLeft, 1}},
+ {3141, {wxStyledTextCtrl, getMarginLeft, 0}},
+ {3142, {wxStyledTextCtrl, setMarginRight, 1}},
+ {3143, {wxStyledTextCtrl, getMarginRight, 0}},
+ {3144, {wxStyledTextCtrl, getModify, 0}},
+ {3145, {wxStyledTextCtrl, setSelection, 2}},
+ {3146, {wxStyledTextCtrl, getSelectedText, 0}},
+ {3147, {wxStyledTextCtrl, getTextRange, 2}},
+ {3148, {wxStyledTextCtrl, hideSelection, 1}},
+ {3149, {wxStyledTextCtrl, lineFromPosition, 1}},
+ {3150, {wxStyledTextCtrl, positionFromLine, 1}},
+ {3151, {wxStyledTextCtrl, lineScroll, 2}},
+ {3152, {wxStyledTextCtrl, ensureCaretVisible, 0}},
+ {3153, {wxStyledTextCtrl, replaceSelection, 1}},
+ {3154, {wxStyledTextCtrl, setReadOnly, 1}},
+ {3155, {wxStyledTextCtrl, canPaste, 0}},
+ {3156, {wxStyledTextCtrl, canUndo, 0}},
+ {3157, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
+ {3158, {wxStyledTextCtrl, undo, 0}},
+ {3159, {wxStyledTextCtrl, cut, 0}},
+ {3160, {wxStyledTextCtrl, copy, 0}},
+ {3161, {wxStyledTextCtrl, paste, 0}},
+ {3162, {wxStyledTextCtrl, clear, 0}},
+ {3163, {wxStyledTextCtrl, setText, 1}},
+ {3164, {wxStyledTextCtrl, getText, 0}},
+ {3165, {wxStyledTextCtrl, getTextLength, 0}},
+ {3166, {wxStyledTextCtrl, getOvertype, 0}},
+ {3167, {wxStyledTextCtrl, setCaretWidth, 1}},
+ {3168, {wxStyledTextCtrl, getCaretWidth, 0}},
+ {3169, {wxStyledTextCtrl, setTargetStart, 1}},
+ {3170, {wxStyledTextCtrl, getTargetStart, 0}},
+ {3171, {wxStyledTextCtrl, setTargetEnd, 1}},
+ {3172, {wxStyledTextCtrl, getTargetEnd, 0}},
+ {3173, {wxStyledTextCtrl, replaceTarget, 1}},
+ {3174, {wxStyledTextCtrl, searchInTarget, 1}},
+ {3175, {wxStyledTextCtrl, setSearchFlags, 1}},
+ {3176, {wxStyledTextCtrl, getSearchFlags, 0}},
+ {3177, {wxStyledTextCtrl, callTipShow, 2}},
+ {3178, {wxStyledTextCtrl, callTipCancel, 0}},
+ {3179, {wxStyledTextCtrl, callTipActive, 0}},
+ {3180, {wxStyledTextCtrl, callTipPosAtStart, 0}},
+ {3181, {wxStyledTextCtrl, callTipSetHighlight, 2}},
+ {3182, {wxStyledTextCtrl, callTipSetBackground, 1}},
+ {3183, {wxStyledTextCtrl, callTipSetForeground, 1}},
+ {3184, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
+ {3185, {wxStyledTextCtrl, callTipUseStyle, 1}},
+ {3186, {wxStyledTextCtrl, visibleFromDocLine, 1}},
+ {3187, {wxStyledTextCtrl, docLineFromVisible, 1}},
+ {3188, {wxStyledTextCtrl, wrapCount, 1}},
+ {3189, {wxStyledTextCtrl, setFoldLevel, 2}},
+ {3190, {wxStyledTextCtrl, getFoldLevel, 1}},
+ {3191, {wxStyledTextCtrl, getLastChild, 2}},
+ {3192, {wxStyledTextCtrl, getFoldParent, 1}},
+ {3193, {wxStyledTextCtrl, showLines, 2}},
+ {3194, {wxStyledTextCtrl, hideLines, 2}},
+ {3195, {wxStyledTextCtrl, getLineVisible, 1}},
+ {3196, {wxStyledTextCtrl, setFoldExpanded, 2}},
+ {3197, {wxStyledTextCtrl, getFoldExpanded, 1}},
+ {3198, {wxStyledTextCtrl, toggleFold, 1}},
+ {3199, {wxStyledTextCtrl, ensureVisible, 1}},
+ {3200, {wxStyledTextCtrl, setFoldFlags, 1}},
+ {3201, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
+ {3202, {wxStyledTextCtrl, setTabIndents, 1}},
+ {3203, {wxStyledTextCtrl, getTabIndents, 0}},
+ {3204, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
+ {3205, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
+ {3206, {wxStyledTextCtrl, setMouseDwellTime, 1}},
+ {3207, {wxStyledTextCtrl, getMouseDwellTime, 0}},
+ {3208, {wxStyledTextCtrl, wordStartPosition, 2}},
+ {3209, {wxStyledTextCtrl, wordEndPosition, 2}},
+ {3210, {wxStyledTextCtrl, setWrapMode, 1}},
+ {3211, {wxStyledTextCtrl, getWrapMode, 0}},
+ {3212, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
+ {3213, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
+ {3214, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
+ {3215, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
+ {3216, {wxStyledTextCtrl, setWrapStartIndent, 1}},
+ {3217, {wxStyledTextCtrl, getWrapStartIndent, 0}},
+ {3218, {wxStyledTextCtrl, setLayoutCache, 1}},
+ {3219, {wxStyledTextCtrl, getLayoutCache, 0}},
+ {3220, {wxStyledTextCtrl, setScrollWidth, 1}},
+ {3221, {wxStyledTextCtrl, getScrollWidth, 0}},
+ {3222, {wxStyledTextCtrl, textWidth, 2}},
+ {3223, {wxStyledTextCtrl, getEndAtLastLine, 0}},
+ {3224, {wxStyledTextCtrl, textHeight, 1}},
+ {3225, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
+ {3226, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
+ {3227, {wxStyledTextCtrl, appendText, 1}},
+ {3228, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
+ {3229, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
+ {3230, {wxStyledTextCtrl, targetFromSelection, 0}},
+ {3231, {wxStyledTextCtrl, linesJoin, 0}},
+ {3232, {wxStyledTextCtrl, linesSplit, 1}},
+ {3233, {wxStyledTextCtrl, setFoldMarginColour, 2}},
+ {3234, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
+ {3235, {wxStyledTextCtrl, lineDown, 0}},
+ {3236, {wxStyledTextCtrl, lineDownExtend, 0}},
+ {3237, {wxStyledTextCtrl, lineUp, 0}},
+ {3238, {wxStyledTextCtrl, lineUpExtend, 0}},
+ {3239, {wxStyledTextCtrl, charLeft, 0}},
+ {3240, {wxStyledTextCtrl, charLeftExtend, 0}},
+ {3241, {wxStyledTextCtrl, charRight, 0}},
+ {3242, {wxStyledTextCtrl, charRightExtend, 0}},
+ {3243, {wxStyledTextCtrl, wordLeft, 0}},
+ {3244, {wxStyledTextCtrl, wordLeftExtend, 0}},
+ {3245, {wxStyledTextCtrl, wordRight, 0}},
+ {3246, {wxStyledTextCtrl, wordRightExtend, 0}},
+ {3247, {wxStyledTextCtrl, home, 0}},
+ {3248, {wxStyledTextCtrl, homeExtend, 0}},
+ {3249, {wxStyledTextCtrl, lineEnd, 0}},
+ {3250, {wxStyledTextCtrl, lineEndExtend, 0}},
+ {3251, {wxStyledTextCtrl, documentStart, 0}},
+ {3252, {wxStyledTextCtrl, documentStartExtend, 0}},
+ {3253, {wxStyledTextCtrl, documentEnd, 0}},
+ {3254, {wxStyledTextCtrl, documentEndExtend, 0}},
+ {3255, {wxStyledTextCtrl, pageUp, 0}},
+ {3256, {wxStyledTextCtrl, pageUpExtend, 0}},
+ {3257, {wxStyledTextCtrl, pageDown, 0}},
+ {3258, {wxStyledTextCtrl, pageDownExtend, 0}},
+ {3259, {wxStyledTextCtrl, editToggleOvertype, 0}},
+ {3260, {wxStyledTextCtrl, cancel, 0}},
+ {3261, {wxStyledTextCtrl, deleteBack, 0}},
+ {3262, {wxStyledTextCtrl, tab, 0}},
+ {3263, {wxStyledTextCtrl, backTab, 0}},
+ {3264, {wxStyledTextCtrl, newLine, 0}},
+ {3265, {wxStyledTextCtrl, formFeed, 0}},
+ {3266, {wxStyledTextCtrl, vCHome, 0}},
+ {3267, {wxStyledTextCtrl, vCHomeExtend, 0}},
+ {3268, {wxStyledTextCtrl, zoomIn, 0}},
+ {3269, {wxStyledTextCtrl, zoomOut, 0}},
+ {3270, {wxStyledTextCtrl, delWordLeft, 0}},
+ {3271, {wxStyledTextCtrl, delWordRight, 0}},
+ {3272, {wxStyledTextCtrl, lineCut, 0}},
+ {3273, {wxStyledTextCtrl, lineDelete, 0}},
+ {3274, {wxStyledTextCtrl, lineTranspose, 0}},
+ {3275, {wxStyledTextCtrl, lineDuplicate, 0}},
+ {3276, {wxStyledTextCtrl, lowerCase, 0}},
+ {3277, {wxStyledTextCtrl, upperCase, 0}},
+ {3278, {wxStyledTextCtrl, lineScrollDown, 0}},
+ {3279, {wxStyledTextCtrl, lineScrollUp, 0}},
+ {3280, {wxStyledTextCtrl, deleteBackNotLine, 0}},
+ {3281, {wxStyledTextCtrl, homeDisplay, 0}},
+ {3282, {wxStyledTextCtrl, homeDisplayExtend, 0}},
+ {3283, {wxStyledTextCtrl, lineEndDisplay, 0}},
+ {3284, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
+ {3285, {wxStyledTextCtrl, homeWrapExtend, 0}},
+ {3286, {wxStyledTextCtrl, lineEndWrap, 0}},
+ {3287, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
+ {3288, {wxStyledTextCtrl, vCHomeWrap, 0}},
+ {3289, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
+ {3290, {wxStyledTextCtrl, lineCopy, 0}},
+ {3291, {wxStyledTextCtrl, moveCaretInsideView, 0}},
+ {3292, {wxStyledTextCtrl, lineLength, 1}},
+ {3293, {wxStyledTextCtrl, braceHighlight, 2}},
+ {3294, {wxStyledTextCtrl, braceBadLight, 1}},
+ {3295, {wxStyledTextCtrl, braceMatch, 1}},
+ {3296, {wxStyledTextCtrl, getViewEOL, 0}},
+ {3297, {wxStyledTextCtrl, setViewEOL, 1}},
+ {3298, {wxStyledTextCtrl, setModEventMask, 1}},
+ {3299, {wxStyledTextCtrl, getEdgeColumn, 0}},
+ {3300, {wxStyledTextCtrl, setEdgeColumn, 1}},
+ {3301, {wxStyledTextCtrl, setEdgeMode, 1}},
+ {3302, {wxStyledTextCtrl, getEdgeMode, 0}},
+ {3303, {wxStyledTextCtrl, getEdgeColour, 0}},
+ {3304, {wxStyledTextCtrl, setEdgeColour, 1}},
+ {3305, {wxStyledTextCtrl, searchAnchor, 0}},
+ {3306, {wxStyledTextCtrl, searchNext, 2}},
+ {3307, {wxStyledTextCtrl, searchPrev, 2}},
+ {3308, {wxStyledTextCtrl, linesOnScreen, 0}},
+ {3309, {wxStyledTextCtrl, usePopUp, 1}},
+ {3310, {wxStyledTextCtrl, selectionIsRectangle, 0}},
+ {3311, {wxStyledTextCtrl, setZoom, 1}},
+ {3312, {wxStyledTextCtrl, getZoom, 0}},
+ {3313, {wxStyledTextCtrl, getModEventMask, 0}},
+ {3314, {wxStyledTextCtrl, setSTCFocus, 1}},
+ {3315, {wxStyledTextCtrl, getSTCFocus, 0}},
+ {3316, {wxStyledTextCtrl, setStatus, 1}},
+ {3317, {wxStyledTextCtrl, getStatus, 0}},
+ {3318, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
+ {3319, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
+ {3320, {wxStyledTextCtrl, setSTCCursor, 1}},
+ {3321, {wxStyledTextCtrl, getSTCCursor, 0}},
+ {3322, {wxStyledTextCtrl, setControlCharSymbol, 1}},
+ {3323, {wxStyledTextCtrl, getControlCharSymbol, 0}},
+ {3324, {wxStyledTextCtrl, wordPartLeft, 0}},
+ {3325, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
+ {3326, {wxStyledTextCtrl, wordPartRight, 0}},
+ {3327, {wxStyledTextCtrl, wordPartRightExtend, 0}},
+ {3328, {wxStyledTextCtrl, setVisiblePolicy, 2}},
+ {3329, {wxStyledTextCtrl, delLineLeft, 0}},
+ {3330, {wxStyledTextCtrl, delLineRight, 0}},
+ {3331, {wxStyledTextCtrl, getXOffset, 0}},
+ {3332, {wxStyledTextCtrl, chooseCaretX, 0}},
+ {3333, {wxStyledTextCtrl, setXCaretPolicy, 2}},
+ {3334, {wxStyledTextCtrl, setYCaretPolicy, 2}},
+ {3335, {wxStyledTextCtrl, getPrintWrapMode, 0}},
+ {3336, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
+ {3337, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
+ {3338, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
+ {3339, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
+ {3340, {wxStyledTextCtrl, paraDownExtend, 0}},
+ {3341, {wxStyledTextCtrl, paraUp, 0}},
+ {3342, {wxStyledTextCtrl, paraUpExtend, 0}},
+ {3343, {wxStyledTextCtrl, positionBefore, 1}},
+ {3344, {wxStyledTextCtrl, positionAfter, 1}},
+ {3345, {wxStyledTextCtrl, copyRange, 2}},
+ {3346, {wxStyledTextCtrl, copyText, 2}},
+ {3347, {wxStyledTextCtrl, setSelectionMode, 1}},
+ {3348, {wxStyledTextCtrl, getSelectionMode, 0}},
+ {3349, {wxStyledTextCtrl, lineDownRectExtend, 0}},
+ {3350, {wxStyledTextCtrl, lineUpRectExtend, 0}},
+ {3351, {wxStyledTextCtrl, charLeftRectExtend, 0}},
+ {3352, {wxStyledTextCtrl, charRightRectExtend, 0}},
+ {3353, {wxStyledTextCtrl, homeRectExtend, 0}},
+ {3354, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
+ {3355, {wxStyledTextCtrl, lineEndRectExtend, 0}},
+ {3356, {wxStyledTextCtrl, pageUpRectExtend, 0}},
+ {3357, {wxStyledTextCtrl, pageDownRectExtend, 0}},
+ {3358, {wxStyledTextCtrl, stutteredPageUp, 0}},
+ {3359, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
+ {3360, {wxStyledTextCtrl, stutteredPageDown, 0}},
+ {3361, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
+ {3362, {wxStyledTextCtrl, wordLeftEnd, 0}},
+ {3363, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
+ {3364, {wxStyledTextCtrl, wordRightEnd, 0}},
+ {3365, {wxStyledTextCtrl, wordRightEndExtend, 0}},
+ {3366, {wxStyledTextCtrl, setWhitespaceChars, 1}},
+ {3367, {wxStyledTextCtrl, setCharsDefault, 0}},
+ {3368, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
+ {3369, {wxStyledTextCtrl, allocate, 1}},
+ {3370, {wxStyledTextCtrl, findColumn, 2}},
+ {3371, {wxStyledTextCtrl, getCaretSticky, 0}},
+ {3372, {wxStyledTextCtrl, setCaretSticky, 1}},
+ {3373, {wxStyledTextCtrl, toggleCaretSticky, 0}},
+ {3374, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
+ {3375, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
+ {3376, {wxStyledTextCtrl, selectionDuplicate, 0}},
+ {3377, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
+ {3378, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
+ {3379, {wxStyledTextCtrl, startRecord, 0}},
+ {3380, {wxStyledTextCtrl, stopRecord, 0}},
+ {3381, {wxStyledTextCtrl, setLexer, 1}},
+ {3382, {wxStyledTextCtrl, getLexer, 0}},
+ {3383, {wxStyledTextCtrl, colourise, 2}},
+ {3384, {wxStyledTextCtrl, setProperty, 2}},
+ {3385, {wxStyledTextCtrl, setKeyWords, 2}},
+ {3386, {wxStyledTextCtrl, setLexerLanguage, 1}},
+ {3387, {wxStyledTextCtrl, getProperty, 1}},
+ {3388, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
+ {3389, {wxStyledTextCtrl, getCurrentLine, 0}},
+ {3390, {wxStyledTextCtrl, styleSetSpec, 2}},
+ {3391, {wxStyledTextCtrl, styleSetFont, 2}},
+ {3392, {wxStyledTextCtrl, styleSetFontAttr, 7}},
+ {3393, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
+ {3394, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
+ {3395, {wxStyledTextCtrl, cmdKeyExecute, 1}},
+ {3396, {wxStyledTextCtrl, setMargins, 2}},
+ {3397, {wxStyledTextCtrl, getSelection, 2}},
+ {3398, {wxStyledTextCtrl, pointFromPosition, 1}},
+ {3399, {wxStyledTextCtrl, scrollToLine, 1}},
+ {3400, {wxStyledTextCtrl, scrollToColumn, 1}},
+ {3401, {wxStyledTextCtrl, setVScrollBar, 1}},
+ {3402, {wxStyledTextCtrl, setHScrollBar, 1}},
+ {3403, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
+ {3404, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
+ {3405, {wxStyledTextCtrl, saveFile, 1}},
+ {3406, {wxStyledTextCtrl, loadFile, 1}},
+ {3407, {wxStyledTextCtrl, doDragOver, 3}},
+ {3408, {wxStyledTextCtrl, doDropText, 3}},
+ {3409, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
+ {3410, {wxStyledTextCtrl, addTextRaw, 1}},
+ {3411, {wxStyledTextCtrl, insertTextRaw, 2}},
+ {3412, {wxStyledTextCtrl, getCurLineRaw, 1}},
+ {3413, {wxStyledTextCtrl, getLineRaw, 1}},
+ {3414, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
+ {3415, {wxStyledTextCtrl, getTextRangeRaw, 2}},
+ {3416, {wxStyledTextCtrl, setTextRaw, 1}},
+ {3417, {wxStyledTextCtrl, getTextRaw, 0}},
+ {3418, {wxStyledTextCtrl, appendTextRaw, 1}},
+ {3419, {wxArtProvider, getBitmap, 2}},
+ {3420, {wxArtProvider, getIcon, 2}},
+ {3421, {wxTreeEvent, getKeyCode, 0}},
+ {3422, {wxTreeEvent, getItem, 0}},
+ {3423, {wxTreeEvent, getKeyEvent, 0}},
+ {3424, {wxTreeEvent, getLabel, 0}},
+ {3425, {wxTreeEvent, getOldItem, 0}},
+ {3426, {wxTreeEvent, getPoint, 0}},
+ {3427, {wxTreeEvent, isEditCancelled, 0}},
+ {3428, {wxTreeEvent, setToolTip, 1}},
+ {3429, {wxNotebookEvent, getOldSelection, 0}},
+ {3430, {wxNotebookEvent, getSelection, 0}},
+ {3431, {wxNotebookEvent, setOldSelection, 1}},
+ {3432, {wxNotebookEvent, setSelection, 1}},
+ {3433, {wxFileDataObject, new, 0}},
+ {3434, {wxFileDataObject, addFile, 1}},
+ {3435, {wxFileDataObject, getFilenames, 0}},
+ {3436, {wxFileDataObject, 'Destroy', undefined}},
+ {3437, {wxTextDataObject, new, 1}},
+ {3438, {wxTextDataObject, getTextLength, 0}},
+ {3439, {wxTextDataObject, getText, 0}},
+ {3440, {wxTextDataObject, setText, 1}},
+ {3441, {wxTextDataObject, 'Destroy', undefined}},
+ {3442, {wxBitmapDataObject, new_1_1, 1}},
+ {3443, {wxBitmapDataObject, new_1_0, 1}},
+ {3444, {wxBitmapDataObject, getBitmap, 0}},
+ {3445, {wxBitmapDataObject, setBitmap, 1}},
+ {3446, {wxBitmapDataObject, 'Destroy', undefined}},
+ {3448, {wxClipboard, new, 0}},
+ {3449, {wxClipboard, destruct, 0}},
+ {3450, {wxClipboard, addData, 1}},
+ {3451, {wxClipboard, clear, 0}},
+ {3452, {wxClipboard, close, 0}},
+ {3453, {wxClipboard, flush, 0}},
+ {3454, {wxClipboard, getData, 1}},
+ {3455, {wxClipboard, isOpened, 0}},
+ {3456, {wxClipboard, open, 0}},
+ {3457, {wxClipboard, setData, 1}},
+ {3459, {wxClipboard, usePrimarySelection, 1}},
+ {3460, {wxClipboard, isSupported, 1}},
+ {3461, {wxClipboard, get, 0}},
+ {3462, {wxSpinEvent, getPosition, 0}},
+ {3463, {wxSpinEvent, setPosition, 1}},
+ {3464, {wxSplitterWindow, new_0, 0}},
+ {3465, {wxSplitterWindow, new_2, 2}},
+ {3466, {wxSplitterWindow, destruct, 0}},
+ {3467, {wxSplitterWindow, create, 2}},
+ {3468, {wxSplitterWindow, getMinimumPaneSize, 0}},
+ {3469, {wxSplitterWindow, getSashGravity, 0}},
+ {3470, {wxSplitterWindow, getSashPosition, 0}},
+ {3471, {wxSplitterWindow, getSplitMode, 0}},
+ {3472, {wxSplitterWindow, getWindow1, 0}},
+ {3473, {wxSplitterWindow, getWindow2, 0}},
+ {3474, {wxSplitterWindow, initialize, 1}},
+ {3475, {wxSplitterWindow, isSplit, 0}},
+ {3476, {wxSplitterWindow, replaceWindow, 2}},
+ {3477, {wxSplitterWindow, setSashGravity, 1}},
+ {3478, {wxSplitterWindow, setSashPosition, 2}},
+ {3479, {wxSplitterWindow, setSashSize, 1}},
+ {3480, {wxSplitterWindow, setMinimumPaneSize, 1}},
+ {3481, {wxSplitterWindow, setSplitMode, 1}},
+ {3482, {wxSplitterWindow, splitHorizontally, 3}},
+ {3483, {wxSplitterWindow, splitVertically, 3}},
+ {3484, {wxSplitterWindow, unsplit, 1}},
+ {3485, {wxSplitterWindow, updateSize, 0}},
+ {3486, {wxSplitterEvent, getSashPosition, 0}},
+ {3487, {wxSplitterEvent, getX, 0}},
+ {3488, {wxSplitterEvent, getY, 0}},
+ {3489, {wxSplitterEvent, getWindowBeingRemoved, 0}},
+ {3490, {wxSplitterEvent, setSashPosition, 1}},
+ {3491, {wxHtmlWindow, new_0, 0}},
+ {3492, {wxHtmlWindow, new_2, 2}},
+ {3493, {wxHtmlWindow, appendToPage, 1}},
+ {3494, {wxHtmlWindow, getOpenedAnchor, 0}},
+ {3495, {wxHtmlWindow, getOpenedPage, 0}},
+ {3496, {wxHtmlWindow, getOpenedPageTitle, 0}},
+ {3497, {wxHtmlWindow, getRelatedFrame, 0}},
+ {3498, {wxHtmlWindow, historyBack, 0}},
+ {3499, {wxHtmlWindow, historyCanBack, 0}},
+ {3500, {wxHtmlWindow, historyCanForward, 0}},
+ {3501, {wxHtmlWindow, historyClear, 0}},
+ {3502, {wxHtmlWindow, historyForward, 0}},
+ {3503, {wxHtmlWindow, loadFile, 1}},
+ {3504, {wxHtmlWindow, loadPage, 1}},
+ {3505, {wxHtmlWindow, selectAll, 0}},
+ {3506, {wxHtmlWindow, selectionToText, 0}},
+ {3507, {wxHtmlWindow, selectLine, 1}},
+ {3508, {wxHtmlWindow, selectWord, 1}},
+ {3509, {wxHtmlWindow, setBorders, 1}},
+ {3510, {wxHtmlWindow, setFonts, 3}},
+ {3511, {wxHtmlWindow, setPage, 1}},
+ {3512, {wxHtmlWindow, setRelatedFrame, 2}},
+ {3513, {wxHtmlWindow, setRelatedStatusBar, 1}},
+ {3514, {wxHtmlWindow, toText, 0}},
+ {3515, {wxHtmlWindow, 'Destroy', undefined}},
+ {3516, {wxHtmlLinkEvent, getLinkInfo, 0}},
+ {3517, {wxSystemSettings, getColour, 1}},
+ {3518, {wxSystemSettings, getFont, 1}},
+ {3519, {wxSystemSettings, getMetric, 2}},
+ {3520, {wxSystemSettings, getScreenType, 0}},
+ {3521, {wxSystemOptions, getOption, 1}},
+ {3522, {wxSystemOptions, getOptionInt, 1}},
+ {3523, {wxSystemOptions, hasOption, 1}},
+ {3524, {wxSystemOptions, isFalse, 1}},
+ {3525, {wxSystemOptions, setOption_2_1, 2}},
+ {3526, {wxSystemOptions, setOption_2_0, 2}},
+ {3527, {wxAuiNotebookEvent, setSelection, 1}},
+ {3528, {wxAuiNotebookEvent, getSelection, 0}},
+ {3529, {wxAuiNotebookEvent, setOldSelection, 1}},
+ {3530, {wxAuiNotebookEvent, getOldSelection, 0}},
+ {3531, {wxAuiNotebookEvent, setDragSource, 1}},
+ {3532, {wxAuiNotebookEvent, getDragSource, 0}},
+ {3533, {wxAuiManagerEvent, setManager, 1}},
+ {3534, {wxAuiManagerEvent, getManager, 0}},
+ {3535, {wxAuiManagerEvent, setPane, 1}},
+ {3536, {wxAuiManagerEvent, getPane, 0}},
+ {3537, {wxAuiManagerEvent, setButton, 1}},
+ {3538, {wxAuiManagerEvent, getButton, 0}},
+ {3539, {wxAuiManagerEvent, setDC, 1}},
+ {3540, {wxAuiManagerEvent, getDC, 0}},
+ {3541, {wxAuiManagerEvent, veto, 1}},
+ {3542, {wxAuiManagerEvent, getVeto, 0}},
+ {3543, {wxAuiManagerEvent, setCanVeto, 1}},
+ {3544, {wxAuiManagerEvent, canVeto, 0}},
+ {3545, {wxLogNull, new, 0}},
+ {3546, {wxLogNull, 'Destroy', undefined}},
+ {3547, {wxTaskBarIcon, new, 0}},
+ {3548, {wxTaskBarIcon, destruct, 0}},
+ {3549, {wxTaskBarIcon, popupMenu, 1}},
+ {3550, {wxTaskBarIcon, removeIcon, 0}},
+ {3551, {wxTaskBarIcon, setIcon, 2}},
+ {3552, {wxLocale, new_0, 0}},
+ {3554, {wxLocale, new_2, 2}},
+ {3555, {wxLocale, destruct, 0}},
+ {3557, {wxLocale, init, 1}},
+ {3558, {wxLocale, addCatalog_1, 1}},
+ {3559, {wxLocale, addCatalog_3, 3}},
+ {3560, {wxLocale, addCatalogLookupPathPrefix, 1}},
+ {3561, {wxLocale, getCanonicalName, 0}},
+ {3562, {wxLocale, getLanguage, 0}},
+ {3563, {wxLocale, getLanguageName, 1}},
+ {3564, {wxLocale, getLocale, 0}},
+ {3565, {wxLocale, getName, 0}},
+ {3566, {wxLocale, getString_2, 2}},
+ {3567, {wxLocale, getString_4, 4}},
+ {3568, {wxLocale, getHeaderValue, 2}},
+ {3569, {wxLocale, getSysName, 0}},
+ {3570, {wxLocale, getSystemEncoding, 0}},
+ {3571, {wxLocale, getSystemEncodingName, 0}},
+ {3572, {wxLocale, getSystemLanguage, 0}},
+ {3573, {wxLocale, isLoaded, 1}},
+ {3574, {wxLocale, isOk, 0}},
+ {3575, {wxActivateEvent, getActive, 0}},
+ {3577, {wxPopupWindow, new_2, 2}},
+ {3578, {wxPopupWindow, new_0, 0}},
+ {3580, {wxPopupWindow, destruct, 0}},
+ {3581, {wxPopupWindow, create, 2}},
+ {3582, {wxPopupWindow, position, 2}},
+ {3583, {wxPopupTransientWindow, new_0, 0}},
+ {3584, {wxPopupTransientWindow, new_2, 2}},
+ {3585, {wxPopupTransientWindow, destruct, 0}},
+ {3586, {wxPopupTransientWindow, popup, 1}},
+ {3587, {wxPopupTransientWindow, dismiss, 0}},
+ {3588, {wxOverlay, new, 0}},
+ {3589, {wxOverlay, destruct, 0}},
+ {3590, {wxOverlay, reset, 0}},
+ {3591, {wxDCOverlay, new_6, 6}},
+ {3592, {wxDCOverlay, new_2, 2}},
+ {3593, {wxDCOverlay, destruct, 0}},
+ {3594, {wxDCOverlay, clear, 0}},
{-1, {mod, func, -1}}
].
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index f5f839ac67..84fa592aaa 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -1454,1922 +1454,1918 @@
-define(wxGauge_new_0, 1602).
-define(wxGauge_new_4, 1603).
-define(wxGauge_Create, 1604).
--define(wxGauge_GetBezelFace, 1605).
--define(wxGauge_GetRange, 1606).
--define(wxGauge_GetShadowWidth, 1607).
--define(wxGauge_GetValue, 1608).
--define(wxGauge_IsVertical, 1609).
--define(wxGauge_SetBezelFace, 1610).
--define(wxGauge_SetRange, 1611).
--define(wxGauge_SetShadowWidth, 1612).
--define(wxGauge_SetValue, 1613).
--define(wxGauge_Pulse, 1614).
--define(wxGauge_destroy, 1615).
--define(wxGenericDirCtrl_new_0, 1616).
--define(wxGenericDirCtrl_new_2, 1617).
--define(wxGenericDirCtrl_destruct, 1618).
--define(wxGenericDirCtrl_Create, 1619).
--define(wxGenericDirCtrl_Init, 1620).
--define(wxGenericDirCtrl_CollapseTree, 1621).
--define(wxGenericDirCtrl_ExpandPath, 1622).
--define(wxGenericDirCtrl_GetDefaultPath, 1623).
--define(wxGenericDirCtrl_GetPath, 1624).
--define(wxGenericDirCtrl_GetFilePath, 1625).
--define(wxGenericDirCtrl_GetFilter, 1626).
--define(wxGenericDirCtrl_GetFilterIndex, 1627).
--define(wxGenericDirCtrl_GetRootId, 1628).
--define(wxGenericDirCtrl_GetTreeCtrl, 1629).
--define(wxGenericDirCtrl_ReCreateTree, 1630).
--define(wxGenericDirCtrl_SetDefaultPath, 1631).
--define(wxGenericDirCtrl_SetFilter, 1632).
--define(wxGenericDirCtrl_SetFilterIndex, 1633).
--define(wxGenericDirCtrl_SetPath, 1634).
--define(wxStaticBox_new_4, 1636).
--define(wxStaticBox_new_0, 1637).
--define(wxStaticBox_Create, 1638).
--define(wxStaticBox_destroy, 1639).
--define(wxStaticLine_new_2, 1641).
--define(wxStaticLine_new_0, 1642).
--define(wxStaticLine_Create, 1643).
--define(wxStaticLine_IsVertical, 1644).
--define(wxStaticLine_GetDefaultSize, 1645).
--define(wxStaticLine_destroy, 1646).
--define(wxListBox_new_3, 1649).
--define(wxListBox_new_0, 1650).
--define(wxListBox_destruct, 1652).
--define(wxListBox_Create, 1654).
--define(wxListBox_Deselect, 1655).
--define(wxListBox_GetSelections, 1656).
--define(wxListBox_InsertItems, 1657).
--define(wxListBox_IsSelected, 1658).
--define(wxListBox_Set, 1659).
--define(wxListBox_HitTest, 1660).
--define(wxListBox_SetFirstItem_1_0, 1661).
--define(wxListBox_SetFirstItem_1_1, 1662).
--define(wxListCtrl_new_0, 1663).
--define(wxListCtrl_new_2, 1664).
--define(wxListCtrl_Arrange, 1665).
--define(wxListCtrl_AssignImageList, 1666).
--define(wxListCtrl_ClearAll, 1667).
--define(wxListCtrl_Create, 1668).
--define(wxListCtrl_DeleteAllItems, 1669).
--define(wxListCtrl_DeleteColumn, 1670).
--define(wxListCtrl_DeleteItem, 1671).
--define(wxListCtrl_EditLabel, 1672).
--define(wxListCtrl_EnsureVisible, 1673).
--define(wxListCtrl_FindItem_3_0, 1674).
--define(wxListCtrl_FindItem_3_1, 1675).
--define(wxListCtrl_GetColumn, 1676).
--define(wxListCtrl_GetColumnCount, 1677).
--define(wxListCtrl_GetColumnWidth, 1678).
--define(wxListCtrl_GetCountPerPage, 1679).
--define(wxListCtrl_GetEditControl, 1680).
--define(wxListCtrl_GetImageList, 1681).
--define(wxListCtrl_GetItem, 1682).
--define(wxListCtrl_GetItemBackgroundColour, 1683).
--define(wxListCtrl_GetItemCount, 1684).
--define(wxListCtrl_GetItemData, 1685).
--define(wxListCtrl_GetItemFont, 1686).
--define(wxListCtrl_GetItemPosition, 1687).
--define(wxListCtrl_GetItemRect, 1688).
--define(wxListCtrl_GetItemSpacing, 1689).
--define(wxListCtrl_GetItemState, 1690).
--define(wxListCtrl_GetItemText, 1691).
--define(wxListCtrl_GetItemTextColour, 1692).
--define(wxListCtrl_GetNextItem, 1693).
--define(wxListCtrl_GetSelectedItemCount, 1694).
--define(wxListCtrl_GetTextColour, 1695).
--define(wxListCtrl_GetTopItem, 1696).
--define(wxListCtrl_GetViewRect, 1697).
--define(wxListCtrl_HitTest, 1698).
--define(wxListCtrl_InsertColumn_2, 1699).
--define(wxListCtrl_InsertColumn_3, 1700).
--define(wxListCtrl_InsertItem_1, 1701).
--define(wxListCtrl_InsertItem_2_1, 1702).
--define(wxListCtrl_InsertItem_2_0, 1703).
--define(wxListCtrl_InsertItem_3, 1704).
--define(wxListCtrl_RefreshItem, 1705).
--define(wxListCtrl_RefreshItems, 1706).
--define(wxListCtrl_ScrollList, 1707).
--define(wxListCtrl_SetBackgroundColour, 1708).
--define(wxListCtrl_SetColumn, 1709).
--define(wxListCtrl_SetColumnWidth, 1710).
--define(wxListCtrl_SetImageList, 1711).
--define(wxListCtrl_SetItem_1, 1712).
--define(wxListCtrl_SetItem_4, 1713).
--define(wxListCtrl_SetItemBackgroundColour, 1714).
--define(wxListCtrl_SetItemCount, 1715).
--define(wxListCtrl_SetItemData, 1716).
--define(wxListCtrl_SetItemFont, 1717).
--define(wxListCtrl_SetItemImage, 1718).
--define(wxListCtrl_SetItemColumnImage, 1719).
--define(wxListCtrl_SetItemPosition, 1720).
--define(wxListCtrl_SetItemState, 1721).
--define(wxListCtrl_SetItemText, 1722).
--define(wxListCtrl_SetItemTextColour, 1723).
--define(wxListCtrl_SetSingleStyle, 1724).
--define(wxListCtrl_SetTextColour, 1725).
--define(wxListCtrl_SetWindowStyleFlag, 1726).
--define(wxListCtrl_SortItems, 1727).
--define(wxListCtrl_destroy, 1728).
--define(wxListView_ClearColumnImage, 1729).
--define(wxListView_Focus, 1730).
--define(wxListView_GetFirstSelected, 1731).
--define(wxListView_GetFocusedItem, 1732).
--define(wxListView_GetNextSelected, 1733).
--define(wxListView_IsSelected, 1734).
--define(wxListView_Select, 1735).
--define(wxListView_SetColumnImage, 1736).
--define(wxListItem_new_0, 1737).
--define(wxListItem_new_1, 1738).
--define(wxListItem_destruct, 1739).
--define(wxListItem_Clear, 1740).
--define(wxListItem_GetAlign, 1741).
--define(wxListItem_GetBackgroundColour, 1742).
--define(wxListItem_GetColumn, 1743).
--define(wxListItem_GetFont, 1744).
--define(wxListItem_GetId, 1745).
--define(wxListItem_GetImage, 1746).
--define(wxListItem_GetMask, 1747).
--define(wxListItem_GetState, 1748).
--define(wxListItem_GetText, 1749).
--define(wxListItem_GetTextColour, 1750).
--define(wxListItem_GetWidth, 1751).
--define(wxListItem_SetAlign, 1752).
--define(wxListItem_SetBackgroundColour, 1753).
--define(wxListItem_SetColumn, 1754).
--define(wxListItem_SetFont, 1755).
--define(wxListItem_SetId, 1756).
--define(wxListItem_SetImage, 1757).
--define(wxListItem_SetMask, 1758).
--define(wxListItem_SetState, 1759).
--define(wxListItem_SetStateMask, 1760).
--define(wxListItem_SetText, 1761).
--define(wxListItem_SetTextColour, 1762).
--define(wxListItem_SetWidth, 1763).
--define(wxListItemAttr_new_0, 1764).
--define(wxListItemAttr_new_3, 1765).
--define(wxListItemAttr_GetBackgroundColour, 1766).
--define(wxListItemAttr_GetFont, 1767).
--define(wxListItemAttr_GetTextColour, 1768).
--define(wxListItemAttr_HasBackgroundColour, 1769).
--define(wxListItemAttr_HasFont, 1770).
--define(wxListItemAttr_HasTextColour, 1771).
--define(wxListItemAttr_SetBackgroundColour, 1772).
--define(wxListItemAttr_SetFont, 1773).
--define(wxListItemAttr_SetTextColour, 1774).
--define(wxListItemAttr_destroy, 1775).
--define(wxImageList_new_0, 1776).
--define(wxImageList_new_3, 1777).
--define(wxImageList_Add_1, 1778).
--define(wxImageList_Add_2_0, 1779).
--define(wxImageList_Add_2_1, 1780).
--define(wxImageList_Create, 1781).
--define(wxImageList_Draw, 1783).
--define(wxImageList_GetBitmap, 1784).
--define(wxImageList_GetIcon, 1785).
--define(wxImageList_GetImageCount, 1786).
--define(wxImageList_GetSize, 1787).
--define(wxImageList_Remove, 1788).
--define(wxImageList_RemoveAll, 1789).
--define(wxImageList_Replace_2, 1790).
--define(wxImageList_Replace_3, 1791).
--define(wxImageList_destroy, 1792).
--define(wxTextAttr_new_0, 1793).
--define(wxTextAttr_new_2, 1794).
--define(wxTextAttr_GetAlignment, 1795).
--define(wxTextAttr_GetBackgroundColour, 1796).
--define(wxTextAttr_GetFont, 1797).
--define(wxTextAttr_GetLeftIndent, 1798).
--define(wxTextAttr_GetLeftSubIndent, 1799).
--define(wxTextAttr_GetRightIndent, 1800).
--define(wxTextAttr_GetTabs, 1801).
--define(wxTextAttr_GetTextColour, 1802).
--define(wxTextAttr_HasBackgroundColour, 1803).
--define(wxTextAttr_HasFont, 1804).
--define(wxTextAttr_HasTextColour, 1805).
--define(wxTextAttr_GetFlags, 1806).
--define(wxTextAttr_IsDefault, 1807).
--define(wxTextAttr_SetAlignment, 1808).
--define(wxTextAttr_SetBackgroundColour, 1809).
--define(wxTextAttr_SetFlags, 1810).
--define(wxTextAttr_SetFont, 1811).
--define(wxTextAttr_SetLeftIndent, 1812).
--define(wxTextAttr_SetRightIndent, 1813).
--define(wxTextAttr_SetTabs, 1814).
--define(wxTextAttr_SetTextColour, 1815).
--define(wxTextAttr_destroy, 1816).
--define(wxTextCtrl_new_3, 1818).
--define(wxTextCtrl_new_0, 1819).
--define(wxTextCtrl_destruct, 1821).
--define(wxTextCtrl_AppendText, 1822).
--define(wxTextCtrl_CanCopy, 1823).
--define(wxTextCtrl_CanCut, 1824).
--define(wxTextCtrl_CanPaste, 1825).
--define(wxTextCtrl_CanRedo, 1826).
--define(wxTextCtrl_CanUndo, 1827).
--define(wxTextCtrl_Clear, 1828).
--define(wxTextCtrl_Copy, 1829).
--define(wxTextCtrl_Create, 1830).
--define(wxTextCtrl_Cut, 1831).
--define(wxTextCtrl_DiscardEdits, 1832).
--define(wxTextCtrl_ChangeValue, 1833).
--define(wxTextCtrl_EmulateKeyPress, 1834).
--define(wxTextCtrl_GetDefaultStyle, 1835).
--define(wxTextCtrl_GetInsertionPoint, 1836).
--define(wxTextCtrl_GetLastPosition, 1837).
--define(wxTextCtrl_GetLineLength, 1838).
--define(wxTextCtrl_GetLineText, 1839).
--define(wxTextCtrl_GetNumberOfLines, 1840).
--define(wxTextCtrl_GetRange, 1841).
--define(wxTextCtrl_GetSelection, 1842).
--define(wxTextCtrl_GetStringSelection, 1843).
--define(wxTextCtrl_GetStyle, 1844).
--define(wxTextCtrl_GetValue, 1845).
--define(wxTextCtrl_IsEditable, 1846).
--define(wxTextCtrl_IsModified, 1847).
--define(wxTextCtrl_IsMultiLine, 1848).
--define(wxTextCtrl_IsSingleLine, 1849).
--define(wxTextCtrl_LoadFile, 1850).
--define(wxTextCtrl_MarkDirty, 1851).
--define(wxTextCtrl_Paste, 1852).
--define(wxTextCtrl_PositionToXY, 1853).
--define(wxTextCtrl_Redo, 1854).
--define(wxTextCtrl_Remove, 1855).
--define(wxTextCtrl_Replace, 1856).
--define(wxTextCtrl_SaveFile, 1857).
--define(wxTextCtrl_SetDefaultStyle, 1858).
--define(wxTextCtrl_SetEditable, 1859).
--define(wxTextCtrl_SetInsertionPoint, 1860).
--define(wxTextCtrl_SetInsertionPointEnd, 1861).
--define(wxTextCtrl_SetMaxLength, 1863).
--define(wxTextCtrl_SetSelection, 1864).
--define(wxTextCtrl_SetStyle, 1865).
--define(wxTextCtrl_SetValue, 1866).
--define(wxTextCtrl_ShowPosition, 1867).
--define(wxTextCtrl_Undo, 1868).
--define(wxTextCtrl_WriteText, 1869).
--define(wxTextCtrl_XYToPosition, 1870).
--define(wxNotebook_new_0, 1873).
--define(wxNotebook_new_3, 1874).
--define(wxNotebook_destruct, 1875).
--define(wxNotebook_AddPage, 1876).
--define(wxNotebook_AdvanceSelection, 1877).
--define(wxNotebook_AssignImageList, 1878).
--define(wxNotebook_Create, 1879).
--define(wxNotebook_DeleteAllPages, 1880).
--define(wxNotebook_DeletePage, 1881).
--define(wxNotebook_RemovePage, 1882).
--define(wxNotebook_GetCurrentPage, 1883).
--define(wxNotebook_GetImageList, 1884).
--define(wxNotebook_GetPage, 1886).
--define(wxNotebook_GetPageCount, 1887).
--define(wxNotebook_GetPageImage, 1888).
--define(wxNotebook_GetPageText, 1889).
--define(wxNotebook_GetRowCount, 1890).
--define(wxNotebook_GetSelection, 1891).
--define(wxNotebook_GetThemeBackgroundColour, 1892).
--define(wxNotebook_HitTest, 1894).
--define(wxNotebook_InsertPage, 1896).
--define(wxNotebook_SetImageList, 1897).
--define(wxNotebook_SetPadding, 1898).
--define(wxNotebook_SetPageSize, 1899).
--define(wxNotebook_SetPageImage, 1900).
--define(wxNotebook_SetPageText, 1901).
--define(wxNotebook_SetSelection, 1902).
--define(wxNotebook_ChangeSelection, 1903).
--define(wxChoicebook_new_0, 1904).
--define(wxChoicebook_new_3, 1905).
--define(wxChoicebook_AddPage, 1906).
--define(wxChoicebook_AdvanceSelection, 1907).
--define(wxChoicebook_AssignImageList, 1908).
--define(wxChoicebook_Create, 1909).
--define(wxChoicebook_DeleteAllPages, 1910).
--define(wxChoicebook_DeletePage, 1911).
--define(wxChoicebook_RemovePage, 1912).
--define(wxChoicebook_GetCurrentPage, 1913).
--define(wxChoicebook_GetImageList, 1914).
--define(wxChoicebook_GetPage, 1916).
--define(wxChoicebook_GetPageCount, 1917).
--define(wxChoicebook_GetPageImage, 1918).
--define(wxChoicebook_GetPageText, 1919).
--define(wxChoicebook_GetSelection, 1920).
--define(wxChoicebook_HitTest, 1921).
--define(wxChoicebook_InsertPage, 1922).
--define(wxChoicebook_SetImageList, 1923).
--define(wxChoicebook_SetPageSize, 1924).
--define(wxChoicebook_SetPageImage, 1925).
--define(wxChoicebook_SetPageText, 1926).
--define(wxChoicebook_SetSelection, 1927).
--define(wxChoicebook_ChangeSelection, 1928).
--define(wxChoicebook_destroy, 1929).
--define(wxToolbook_new_0, 1930).
--define(wxToolbook_new_3, 1931).
--define(wxToolbook_AddPage, 1932).
--define(wxToolbook_AdvanceSelection, 1933).
--define(wxToolbook_AssignImageList, 1934).
--define(wxToolbook_Create, 1935).
--define(wxToolbook_DeleteAllPages, 1936).
--define(wxToolbook_DeletePage, 1937).
--define(wxToolbook_RemovePage, 1938).
--define(wxToolbook_GetCurrentPage, 1939).
--define(wxToolbook_GetImageList, 1940).
--define(wxToolbook_GetPage, 1942).
--define(wxToolbook_GetPageCount, 1943).
--define(wxToolbook_GetPageImage, 1944).
--define(wxToolbook_GetPageText, 1945).
--define(wxToolbook_GetSelection, 1946).
--define(wxToolbook_HitTest, 1948).
--define(wxToolbook_InsertPage, 1949).
--define(wxToolbook_SetImageList, 1950).
--define(wxToolbook_SetPageSize, 1951).
--define(wxToolbook_SetPageImage, 1952).
--define(wxToolbook_SetPageText, 1953).
--define(wxToolbook_SetSelection, 1954).
--define(wxToolbook_ChangeSelection, 1955).
--define(wxToolbook_destroy, 1956).
--define(wxListbook_new_0, 1957).
--define(wxListbook_new_3, 1958).
--define(wxListbook_AddPage, 1959).
--define(wxListbook_AdvanceSelection, 1960).
--define(wxListbook_AssignImageList, 1961).
--define(wxListbook_Create, 1962).
--define(wxListbook_DeleteAllPages, 1963).
--define(wxListbook_DeletePage, 1964).
--define(wxListbook_RemovePage, 1965).
--define(wxListbook_GetCurrentPage, 1966).
--define(wxListbook_GetImageList, 1967).
--define(wxListbook_GetPage, 1969).
--define(wxListbook_GetPageCount, 1970).
--define(wxListbook_GetPageImage, 1971).
--define(wxListbook_GetPageText, 1972).
--define(wxListbook_GetSelection, 1973).
--define(wxListbook_HitTest, 1975).
--define(wxListbook_InsertPage, 1976).
--define(wxListbook_SetImageList, 1977).
--define(wxListbook_SetPageSize, 1978).
--define(wxListbook_SetPageImage, 1979).
--define(wxListbook_SetPageText, 1980).
--define(wxListbook_SetSelection, 1981).
--define(wxListbook_ChangeSelection, 1982).
--define(wxListbook_destroy, 1983).
--define(wxTreebook_new_0, 1984).
--define(wxTreebook_new_3, 1985).
--define(wxTreebook_AddPage, 1986).
--define(wxTreebook_AdvanceSelection, 1987).
--define(wxTreebook_AssignImageList, 1988).
--define(wxTreebook_Create, 1989).
--define(wxTreebook_DeleteAllPages, 1990).
--define(wxTreebook_DeletePage, 1991).
--define(wxTreebook_RemovePage, 1992).
--define(wxTreebook_GetCurrentPage, 1993).
--define(wxTreebook_GetImageList, 1994).
--define(wxTreebook_GetPage, 1996).
--define(wxTreebook_GetPageCount, 1997).
--define(wxTreebook_GetPageImage, 1998).
--define(wxTreebook_GetPageText, 1999).
--define(wxTreebook_GetSelection, 2000).
--define(wxTreebook_ExpandNode, 2001).
--define(wxTreebook_IsNodeExpanded, 2002).
--define(wxTreebook_HitTest, 2004).
--define(wxTreebook_InsertPage, 2005).
--define(wxTreebook_InsertSubPage, 2006).
--define(wxTreebook_SetImageList, 2007).
--define(wxTreebook_SetPageSize, 2008).
--define(wxTreebook_SetPageImage, 2009).
--define(wxTreebook_SetPageText, 2010).
--define(wxTreebook_SetSelection, 2011).
--define(wxTreebook_ChangeSelection, 2012).
--define(wxTreebook_destroy, 2013).
--define(wxTreeCtrl_new_2, 2016).
--define(wxTreeCtrl_new_0, 2017).
--define(wxTreeCtrl_destruct, 2019).
--define(wxTreeCtrl_AddRoot, 2020).
--define(wxTreeCtrl_AppendItem, 2021).
--define(wxTreeCtrl_AssignImageList, 2022).
--define(wxTreeCtrl_AssignStateImageList, 2023).
--define(wxTreeCtrl_Collapse, 2024).
--define(wxTreeCtrl_CollapseAndReset, 2025).
--define(wxTreeCtrl_Create, 2026).
--define(wxTreeCtrl_Delete, 2027).
--define(wxTreeCtrl_DeleteAllItems, 2028).
--define(wxTreeCtrl_DeleteChildren, 2029).
--define(wxTreeCtrl_EditLabel, 2030).
--define(wxTreeCtrl_EnsureVisible, 2031).
--define(wxTreeCtrl_Expand, 2032).
--define(wxTreeCtrl_GetBoundingRect, 2033).
--define(wxTreeCtrl_GetChildrenCount, 2035).
--define(wxTreeCtrl_GetCount, 2036).
--define(wxTreeCtrl_GetEditControl, 2037).
--define(wxTreeCtrl_GetFirstChild, 2038).
--define(wxTreeCtrl_GetNextChild, 2039).
--define(wxTreeCtrl_GetFirstVisibleItem, 2040).
--define(wxTreeCtrl_GetImageList, 2041).
--define(wxTreeCtrl_GetIndent, 2042).
--define(wxTreeCtrl_GetItemBackgroundColour, 2043).
--define(wxTreeCtrl_GetItemData, 2044).
--define(wxTreeCtrl_GetItemFont, 2045).
--define(wxTreeCtrl_GetItemImage_1, 2046).
--define(wxTreeCtrl_GetItemImage_2, 2047).
--define(wxTreeCtrl_GetItemText, 2048).
--define(wxTreeCtrl_GetItemTextColour, 2049).
--define(wxTreeCtrl_GetLastChild, 2050).
--define(wxTreeCtrl_GetNextSibling, 2051).
--define(wxTreeCtrl_GetNextVisible, 2052).
--define(wxTreeCtrl_GetItemParent, 2053).
--define(wxTreeCtrl_GetPrevSibling, 2054).
--define(wxTreeCtrl_GetPrevVisible, 2055).
--define(wxTreeCtrl_GetRootItem, 2056).
--define(wxTreeCtrl_GetSelection, 2057).
--define(wxTreeCtrl_GetSelections, 2058).
--define(wxTreeCtrl_GetStateImageList, 2059).
--define(wxTreeCtrl_HitTest, 2060).
--define(wxTreeCtrl_InsertItem, 2062).
--define(wxTreeCtrl_IsBold, 2063).
--define(wxTreeCtrl_IsExpanded, 2064).
--define(wxTreeCtrl_IsSelected, 2065).
--define(wxTreeCtrl_IsVisible, 2066).
--define(wxTreeCtrl_ItemHasChildren, 2067).
--define(wxTreeCtrl_IsTreeItemIdOk, 2068).
--define(wxTreeCtrl_PrependItem, 2069).
--define(wxTreeCtrl_ScrollTo, 2070).
--define(wxTreeCtrl_SelectItem_1, 2071).
--define(wxTreeCtrl_SelectItem_2, 2072).
--define(wxTreeCtrl_SetIndent, 2073).
--define(wxTreeCtrl_SetImageList, 2074).
--define(wxTreeCtrl_SetItemBackgroundColour, 2075).
--define(wxTreeCtrl_SetItemBold, 2076).
--define(wxTreeCtrl_SetItemData, 2077).
--define(wxTreeCtrl_SetItemDropHighlight, 2078).
--define(wxTreeCtrl_SetItemFont, 2079).
--define(wxTreeCtrl_SetItemHasChildren, 2080).
--define(wxTreeCtrl_SetItemImage_2, 2081).
--define(wxTreeCtrl_SetItemImage_3, 2082).
--define(wxTreeCtrl_SetItemText, 2083).
--define(wxTreeCtrl_SetItemTextColour, 2084).
--define(wxTreeCtrl_SetStateImageList, 2085).
--define(wxTreeCtrl_SetWindowStyle, 2086).
--define(wxTreeCtrl_SortChildren, 2087).
--define(wxTreeCtrl_Toggle, 2088).
--define(wxTreeCtrl_ToggleItemSelection, 2089).
--define(wxTreeCtrl_Unselect, 2090).
--define(wxTreeCtrl_UnselectAll, 2091).
--define(wxTreeCtrl_UnselectItem, 2092).
--define(wxScrollBar_new_0, 2093).
--define(wxScrollBar_new_3, 2094).
--define(wxScrollBar_destruct, 2095).
--define(wxScrollBar_Create, 2096).
--define(wxScrollBar_GetRange, 2097).
--define(wxScrollBar_GetPageSize, 2098).
--define(wxScrollBar_GetThumbPosition, 2099).
--define(wxScrollBar_GetThumbSize, 2100).
--define(wxScrollBar_SetThumbPosition, 2101).
--define(wxScrollBar_SetScrollbar, 2102).
--define(wxSpinButton_new_2, 2104).
--define(wxSpinButton_new_0, 2105).
--define(wxSpinButton_Create, 2106).
--define(wxSpinButton_GetMax, 2107).
--define(wxSpinButton_GetMin, 2108).
--define(wxSpinButton_GetValue, 2109).
--define(wxSpinButton_SetRange, 2110).
--define(wxSpinButton_SetValue, 2111).
--define(wxSpinButton_destroy, 2112).
--define(wxSpinCtrl_new_0, 2113).
--define(wxSpinCtrl_new_2, 2114).
--define(wxSpinCtrl_Create, 2116).
--define(wxSpinCtrl_SetValue_1_1, 2119).
--define(wxSpinCtrl_SetValue_1_0, 2120).
--define(wxSpinCtrl_GetValue, 2122).
--define(wxSpinCtrl_SetRange, 2124).
--define(wxSpinCtrl_SetSelection, 2125).
--define(wxSpinCtrl_GetMin, 2127).
--define(wxSpinCtrl_GetMax, 2129).
--define(wxSpinCtrl_destroy, 2130).
--define(wxStaticText_new_0, 2131).
--define(wxStaticText_new_4, 2132).
--define(wxStaticText_Create, 2133).
--define(wxStaticText_GetLabel, 2134).
--define(wxStaticText_SetLabel, 2135).
--define(wxStaticText_Wrap, 2136).
--define(wxStaticText_destroy, 2137).
--define(wxStaticBitmap_new_0, 2138).
--define(wxStaticBitmap_new_4, 2139).
--define(wxStaticBitmap_Create, 2140).
--define(wxStaticBitmap_GetBitmap, 2141).
--define(wxStaticBitmap_SetBitmap, 2142).
--define(wxStaticBitmap_destroy, 2143).
--define(wxRadioBox_new, 2144).
--define(wxRadioBox_destruct, 2146).
--define(wxRadioBox_Create, 2147).
--define(wxRadioBox_Enable_2, 2148).
--define(wxRadioBox_Enable_1, 2149).
--define(wxRadioBox_GetSelection, 2150).
--define(wxRadioBox_GetString, 2151).
--define(wxRadioBox_SetSelection, 2152).
--define(wxRadioBox_Show_2, 2153).
--define(wxRadioBox_Show_1, 2154).
--define(wxRadioBox_GetColumnCount, 2155).
--define(wxRadioBox_GetItemHelpText, 2156).
--define(wxRadioBox_GetItemToolTip, 2157).
--define(wxRadioBox_GetItemFromPoint, 2159).
--define(wxRadioBox_GetRowCount, 2160).
--define(wxRadioBox_IsItemEnabled, 2161).
--define(wxRadioBox_IsItemShown, 2162).
--define(wxRadioBox_SetItemHelpText, 2163).
--define(wxRadioBox_SetItemToolTip, 2164).
--define(wxRadioButton_new_0, 2165).
--define(wxRadioButton_new_4, 2166).
--define(wxRadioButton_Create, 2167).
--define(wxRadioButton_GetValue, 2168).
--define(wxRadioButton_SetValue, 2169).
--define(wxRadioButton_destroy, 2170).
--define(wxSlider_new_6, 2172).
--define(wxSlider_new_0, 2173).
--define(wxSlider_Create, 2174).
--define(wxSlider_GetLineSize, 2175).
--define(wxSlider_GetMax, 2176).
--define(wxSlider_GetMin, 2177).
--define(wxSlider_GetPageSize, 2178).
--define(wxSlider_GetThumbLength, 2179).
--define(wxSlider_GetValue, 2180).
--define(wxSlider_SetLineSize, 2181).
--define(wxSlider_SetPageSize, 2182).
--define(wxSlider_SetRange, 2183).
--define(wxSlider_SetThumbLength, 2184).
--define(wxSlider_SetValue, 2185).
--define(wxSlider_destroy, 2186).
--define(wxDialog_new_4, 2188).
--define(wxDialog_new_0, 2189).
--define(wxDialog_destruct, 2191).
--define(wxDialog_Create, 2192).
--define(wxDialog_CreateButtonSizer, 2193).
--define(wxDialog_CreateStdDialogButtonSizer, 2194).
--define(wxDialog_EndModal, 2195).
--define(wxDialog_GetAffirmativeId, 2196).
--define(wxDialog_GetReturnCode, 2197).
--define(wxDialog_IsModal, 2198).
--define(wxDialog_SetAffirmativeId, 2199).
--define(wxDialog_SetReturnCode, 2200).
--define(wxDialog_Show, 2201).
--define(wxDialog_ShowModal, 2202).
--define(wxColourDialog_new_0, 2203).
--define(wxColourDialog_new_2, 2204).
--define(wxColourDialog_destruct, 2205).
--define(wxColourDialog_Create, 2206).
--define(wxColourDialog_GetColourData, 2207).
--define(wxColourData_new_0, 2208).
--define(wxColourData_new_1, 2209).
--define(wxColourData_destruct, 2210).
--define(wxColourData_GetChooseFull, 2211).
--define(wxColourData_GetColour, 2212).
--define(wxColourData_GetCustomColour, 2214).
--define(wxColourData_SetChooseFull, 2215).
--define(wxColourData_SetColour, 2216).
--define(wxColourData_SetCustomColour, 2217).
--define(wxPalette_new_0, 2218).
--define(wxPalette_new_4, 2219).
--define(wxPalette_destruct, 2221).
--define(wxPalette_Create, 2222).
--define(wxPalette_GetColoursCount, 2223).
--define(wxPalette_GetPixel, 2224).
--define(wxPalette_GetRGB, 2225).
--define(wxPalette_IsOk, 2226).
--define(wxDirDialog_new, 2230).
--define(wxDirDialog_destruct, 2231).
--define(wxDirDialog_GetPath, 2232).
--define(wxDirDialog_GetMessage, 2233).
--define(wxDirDialog_SetMessage, 2234).
--define(wxDirDialog_SetPath, 2235).
--define(wxFileDialog_new, 2239).
--define(wxFileDialog_destruct, 2240).
--define(wxFileDialog_GetDirectory, 2241).
--define(wxFileDialog_GetFilename, 2242).
--define(wxFileDialog_GetFilenames, 2243).
--define(wxFileDialog_GetFilterIndex, 2244).
--define(wxFileDialog_GetMessage, 2245).
--define(wxFileDialog_GetPath, 2246).
--define(wxFileDialog_GetPaths, 2247).
--define(wxFileDialog_GetWildcard, 2248).
--define(wxFileDialog_SetDirectory, 2249).
--define(wxFileDialog_SetFilename, 2250).
--define(wxFileDialog_SetFilterIndex, 2251).
--define(wxFileDialog_SetMessage, 2252).
--define(wxFileDialog_SetPath, 2253).
--define(wxFileDialog_SetWildcard, 2254).
--define(wxPickerBase_SetInternalMargin, 2255).
--define(wxPickerBase_GetInternalMargin, 2256).
--define(wxPickerBase_SetTextCtrlProportion, 2257).
--define(wxPickerBase_SetPickerCtrlProportion, 2258).
--define(wxPickerBase_GetTextCtrlProportion, 2259).
--define(wxPickerBase_GetPickerCtrlProportion, 2260).
--define(wxPickerBase_HasTextCtrl, 2261).
--define(wxPickerBase_GetTextCtrl, 2262).
--define(wxPickerBase_IsTextCtrlGrowable, 2263).
--define(wxPickerBase_SetPickerCtrlGrowable, 2264).
--define(wxPickerBase_SetTextCtrlGrowable, 2265).
--define(wxPickerBase_IsPickerCtrlGrowable, 2266).
--define(wxFilePickerCtrl_new_0, 2267).
--define(wxFilePickerCtrl_new_3, 2268).
--define(wxFilePickerCtrl_Create, 2269).
--define(wxFilePickerCtrl_GetPath, 2270).
--define(wxFilePickerCtrl_SetPath, 2271).
--define(wxFilePickerCtrl_destroy, 2272).
--define(wxDirPickerCtrl_new_0, 2273).
--define(wxDirPickerCtrl_new_3, 2274).
--define(wxDirPickerCtrl_Create, 2275).
--define(wxDirPickerCtrl_GetPath, 2276).
--define(wxDirPickerCtrl_SetPath, 2277).
--define(wxDirPickerCtrl_destroy, 2278).
--define(wxColourPickerCtrl_new_0, 2279).
--define(wxColourPickerCtrl_new_3, 2280).
--define(wxColourPickerCtrl_Create, 2281).
--define(wxColourPickerCtrl_GetColour, 2282).
--define(wxColourPickerCtrl_SetColour_1_1, 2283).
--define(wxColourPickerCtrl_SetColour_1_0, 2284).
--define(wxColourPickerCtrl_destroy, 2285).
--define(wxDatePickerCtrl_new_0, 2286).
--define(wxDatePickerCtrl_new_3, 2287).
--define(wxDatePickerCtrl_GetRange, 2288).
--define(wxDatePickerCtrl_GetValue, 2289).
--define(wxDatePickerCtrl_SetRange, 2290).
--define(wxDatePickerCtrl_SetValue, 2291).
--define(wxDatePickerCtrl_destroy, 2292).
--define(wxFontPickerCtrl_new_0, 2293).
--define(wxFontPickerCtrl_new_3, 2294).
--define(wxFontPickerCtrl_Create, 2295).
--define(wxFontPickerCtrl_GetSelectedFont, 2296).
--define(wxFontPickerCtrl_SetSelectedFont, 2297).
--define(wxFontPickerCtrl_GetMaxPointSize, 2298).
--define(wxFontPickerCtrl_SetMaxPointSize, 2299).
--define(wxFontPickerCtrl_destroy, 2300).
--define(wxFindReplaceDialog_new_0, 2303).
--define(wxFindReplaceDialog_new_4, 2304).
--define(wxFindReplaceDialog_destruct, 2305).
--define(wxFindReplaceDialog_Create, 2306).
--define(wxFindReplaceDialog_GetData, 2307).
--define(wxFindReplaceData_new_0, 2308).
--define(wxFindReplaceData_new_1, 2309).
--define(wxFindReplaceData_GetFindString, 2310).
--define(wxFindReplaceData_GetReplaceString, 2311).
--define(wxFindReplaceData_GetFlags, 2312).
--define(wxFindReplaceData_SetFlags, 2313).
--define(wxFindReplaceData_SetFindString, 2314).
--define(wxFindReplaceData_SetReplaceString, 2315).
--define(wxFindReplaceData_destroy, 2316).
--define(wxMultiChoiceDialog_new_0, 2317).
--define(wxMultiChoiceDialog_new_5, 2319).
--define(wxMultiChoiceDialog_GetSelections, 2320).
--define(wxMultiChoiceDialog_SetSelections, 2321).
--define(wxMultiChoiceDialog_destroy, 2322).
--define(wxSingleChoiceDialog_new_0, 2323).
--define(wxSingleChoiceDialog_new_5, 2325).
--define(wxSingleChoiceDialog_GetSelection, 2326).
--define(wxSingleChoiceDialog_GetStringSelection, 2327).
--define(wxSingleChoiceDialog_SetSelection, 2328).
--define(wxSingleChoiceDialog_destroy, 2329).
--define(wxTextEntryDialog_new, 2330).
--define(wxTextEntryDialog_GetValue, 2331).
--define(wxTextEntryDialog_SetValue, 2332).
--define(wxTextEntryDialog_destroy, 2333).
--define(wxPasswordEntryDialog_new, 2334).
--define(wxPasswordEntryDialog_destroy, 2335).
--define(wxFontData_new_0, 2336).
--define(wxFontData_new_1, 2337).
--define(wxFontData_destruct, 2338).
--define(wxFontData_EnableEffects, 2339).
--define(wxFontData_GetAllowSymbols, 2340).
--define(wxFontData_GetColour, 2341).
--define(wxFontData_GetChosenFont, 2342).
--define(wxFontData_GetEnableEffects, 2343).
--define(wxFontData_GetInitialFont, 2344).
--define(wxFontData_GetShowHelp, 2345).
--define(wxFontData_SetAllowSymbols, 2346).
--define(wxFontData_SetChosenFont, 2347).
--define(wxFontData_SetColour, 2348).
--define(wxFontData_SetInitialFont, 2349).
--define(wxFontData_SetRange, 2350).
--define(wxFontData_SetShowHelp, 2351).
--define(wxFontDialog_new_0, 2355).
--define(wxFontDialog_new_2, 2357).
--define(wxFontDialog_Create, 2359).
--define(wxFontDialog_GetFontData, 2360).
--define(wxFontDialog_destroy, 2362).
--define(wxProgressDialog_new, 2363).
--define(wxProgressDialog_destruct, 2364).
--define(wxProgressDialog_Resume, 2365).
--define(wxProgressDialog_Update_2, 2366).
--define(wxProgressDialog_Update_0, 2367).
--define(wxMessageDialog_new, 2368).
--define(wxMessageDialog_destruct, 2369).
--define(wxPageSetupDialog_new, 2370).
--define(wxPageSetupDialog_destruct, 2371).
--define(wxPageSetupDialog_GetPageSetupData, 2372).
--define(wxPageSetupDialog_ShowModal, 2373).
--define(wxPageSetupDialogData_new_0, 2374).
--define(wxPageSetupDialogData_new_1_0, 2375).
--define(wxPageSetupDialogData_new_1_1, 2376).
--define(wxPageSetupDialogData_destruct, 2377).
--define(wxPageSetupDialogData_EnableHelp, 2378).
--define(wxPageSetupDialogData_EnableMargins, 2379).
--define(wxPageSetupDialogData_EnableOrientation, 2380).
--define(wxPageSetupDialogData_EnablePaper, 2381).
--define(wxPageSetupDialogData_EnablePrinter, 2382).
--define(wxPageSetupDialogData_GetDefaultMinMargins, 2383).
--define(wxPageSetupDialogData_GetEnableMargins, 2384).
--define(wxPageSetupDialogData_GetEnableOrientation, 2385).
--define(wxPageSetupDialogData_GetEnablePaper, 2386).
--define(wxPageSetupDialogData_GetEnablePrinter, 2387).
--define(wxPageSetupDialogData_GetEnableHelp, 2388).
--define(wxPageSetupDialogData_GetDefaultInfo, 2389).
--define(wxPageSetupDialogData_GetMarginTopLeft, 2390).
--define(wxPageSetupDialogData_GetMarginBottomRight, 2391).
--define(wxPageSetupDialogData_GetMinMarginTopLeft, 2392).
--define(wxPageSetupDialogData_GetMinMarginBottomRight, 2393).
--define(wxPageSetupDialogData_GetPaperId, 2394).
--define(wxPageSetupDialogData_GetPaperSize, 2395).
--define(wxPageSetupDialogData_GetPrintData, 2397).
--define(wxPageSetupDialogData_IsOk, 2398).
--define(wxPageSetupDialogData_SetDefaultInfo, 2399).
--define(wxPageSetupDialogData_SetDefaultMinMargins, 2400).
--define(wxPageSetupDialogData_SetMarginTopLeft, 2401).
--define(wxPageSetupDialogData_SetMarginBottomRight, 2402).
--define(wxPageSetupDialogData_SetMinMarginTopLeft, 2403).
--define(wxPageSetupDialogData_SetMinMarginBottomRight, 2404).
--define(wxPageSetupDialogData_SetPaperId, 2405).
--define(wxPageSetupDialogData_SetPaperSize_1_1, 2406).
--define(wxPageSetupDialogData_SetPaperSize_1_0, 2407).
--define(wxPageSetupDialogData_SetPrintData, 2408).
--define(wxPrintDialog_new_2_0, 2409).
--define(wxPrintDialog_new_2_1, 2410).
--define(wxPrintDialog_destruct, 2411).
--define(wxPrintDialog_GetPrintDialogData, 2412).
--define(wxPrintDialog_GetPrintDC, 2413).
--define(wxPrintDialogData_new_0, 2414).
--define(wxPrintDialogData_new_1_1, 2415).
--define(wxPrintDialogData_new_1_0, 2416).
--define(wxPrintDialogData_destruct, 2417).
--define(wxPrintDialogData_EnableHelp, 2418).
--define(wxPrintDialogData_EnablePageNumbers, 2419).
--define(wxPrintDialogData_EnablePrintToFile, 2420).
--define(wxPrintDialogData_EnableSelection, 2421).
--define(wxPrintDialogData_GetAllPages, 2422).
--define(wxPrintDialogData_GetCollate, 2423).
--define(wxPrintDialogData_GetFromPage, 2424).
--define(wxPrintDialogData_GetMaxPage, 2425).
--define(wxPrintDialogData_GetMinPage, 2426).
--define(wxPrintDialogData_GetNoCopies, 2427).
--define(wxPrintDialogData_GetPrintData, 2428).
--define(wxPrintDialogData_GetPrintToFile, 2429).
--define(wxPrintDialogData_GetSelection, 2430).
--define(wxPrintDialogData_GetToPage, 2431).
--define(wxPrintDialogData_IsOk, 2432).
--define(wxPrintDialogData_SetCollate, 2433).
--define(wxPrintDialogData_SetFromPage, 2434).
--define(wxPrintDialogData_SetMaxPage, 2435).
--define(wxPrintDialogData_SetMinPage, 2436).
--define(wxPrintDialogData_SetNoCopies, 2437).
--define(wxPrintDialogData_SetPrintData, 2438).
--define(wxPrintDialogData_SetPrintToFile, 2439).
--define(wxPrintDialogData_SetSelection, 2440).
--define(wxPrintDialogData_SetToPage, 2441).
--define(wxPrintData_new_0, 2442).
--define(wxPrintData_new_1, 2443).
--define(wxPrintData_destruct, 2444).
--define(wxPrintData_GetCollate, 2445).
--define(wxPrintData_GetBin, 2446).
--define(wxPrintData_GetColour, 2447).
--define(wxPrintData_GetDuplex, 2448).
--define(wxPrintData_GetNoCopies, 2449).
--define(wxPrintData_GetOrientation, 2450).
--define(wxPrintData_GetPaperId, 2451).
--define(wxPrintData_GetPrinterName, 2452).
--define(wxPrintData_GetQuality, 2453).
--define(wxPrintData_IsOk, 2454).
--define(wxPrintData_SetBin, 2455).
--define(wxPrintData_SetCollate, 2456).
--define(wxPrintData_SetColour, 2457).
--define(wxPrintData_SetDuplex, 2458).
--define(wxPrintData_SetNoCopies, 2459).
--define(wxPrintData_SetOrientation, 2460).
--define(wxPrintData_SetPaperId, 2461).
--define(wxPrintData_SetPrinterName, 2462).
--define(wxPrintData_SetQuality, 2463).
--define(wxPrintPreview_new_2, 2466).
--define(wxPrintPreview_new_3, 2467).
--define(wxPrintPreview_destruct, 2469).
--define(wxPrintPreview_GetCanvas, 2470).
--define(wxPrintPreview_GetCurrentPage, 2471).
--define(wxPrintPreview_GetFrame, 2472).
--define(wxPrintPreview_GetMaxPage, 2473).
--define(wxPrintPreview_GetMinPage, 2474).
--define(wxPrintPreview_GetPrintout, 2475).
--define(wxPrintPreview_GetPrintoutForPrinting, 2476).
--define(wxPrintPreview_IsOk, 2477).
--define(wxPrintPreview_PaintPage, 2478).
--define(wxPrintPreview_Print, 2479).
--define(wxPrintPreview_RenderPage, 2480).
--define(wxPrintPreview_SetCanvas, 2481).
--define(wxPrintPreview_SetCurrentPage, 2482).
--define(wxPrintPreview_SetFrame, 2483).
--define(wxPrintPreview_SetPrintout, 2484).
--define(wxPrintPreview_SetZoom, 2485).
--define(wxPreviewFrame_new, 2486).
--define(wxPreviewFrame_destruct, 2487).
--define(wxPreviewFrame_CreateControlBar, 2488).
--define(wxPreviewFrame_CreateCanvas, 2489).
--define(wxPreviewFrame_Initialize, 2490).
--define(wxPreviewFrame_OnCloseWindow, 2491).
--define(wxPreviewControlBar_new, 2492).
--define(wxPreviewControlBar_destruct, 2493).
--define(wxPreviewControlBar_CreateButtons, 2494).
--define(wxPreviewControlBar_GetPrintPreview, 2495).
--define(wxPreviewControlBar_GetZoomControl, 2496).
--define(wxPreviewControlBar_SetZoomControl, 2497).
--define(wxPrinter_new, 2499).
--define(wxPrinter_CreateAbortWindow, 2500).
--define(wxPrinter_GetAbort, 2501).
--define(wxPrinter_GetLastError, 2502).
--define(wxPrinter_GetPrintDialogData, 2503).
--define(wxPrinter_Print, 2504).
--define(wxPrinter_PrintDialog, 2505).
--define(wxPrinter_ReportError, 2506).
--define(wxPrinter_Setup, 2507).
--define(wxPrinter_destroy, 2508).
--define(wxXmlResource_new_1, 2509).
--define(wxXmlResource_new_2, 2510).
--define(wxXmlResource_destruct, 2511).
--define(wxXmlResource_AttachUnknownControl, 2512).
--define(wxXmlResource_ClearHandlers, 2513).
--define(wxXmlResource_CompareVersion, 2514).
--define(wxXmlResource_Get, 2515).
--define(wxXmlResource_GetFlags, 2516).
--define(wxXmlResource_GetVersion, 2517).
--define(wxXmlResource_GetXRCID, 2518).
--define(wxXmlResource_InitAllHandlers, 2519).
--define(wxXmlResource_Load, 2520).
--define(wxXmlResource_LoadBitmap, 2521).
--define(wxXmlResource_LoadDialog_2, 2522).
--define(wxXmlResource_LoadDialog_3, 2523).
--define(wxXmlResource_LoadFrame_2, 2524).
--define(wxXmlResource_LoadFrame_3, 2525).
--define(wxXmlResource_LoadIcon, 2526).
--define(wxXmlResource_LoadMenu, 2527).
--define(wxXmlResource_LoadMenuBar_2, 2528).
--define(wxXmlResource_LoadMenuBar_1, 2529).
--define(wxXmlResource_LoadPanel_2, 2530).
--define(wxXmlResource_LoadPanel_3, 2531).
--define(wxXmlResource_LoadToolBar, 2532).
--define(wxXmlResource_Set, 2533).
--define(wxXmlResource_SetFlags, 2534).
--define(wxXmlResource_Unload, 2535).
--define(wxXmlResource_xrcctrl, 2536).
--define(wxHtmlEasyPrinting_new, 2537).
--define(wxHtmlEasyPrinting_destruct, 2538).
--define(wxHtmlEasyPrinting_GetPrintData, 2539).
--define(wxHtmlEasyPrinting_GetPageSetupData, 2540).
--define(wxHtmlEasyPrinting_PreviewFile, 2541).
--define(wxHtmlEasyPrinting_PreviewText, 2542).
--define(wxHtmlEasyPrinting_PrintFile, 2543).
--define(wxHtmlEasyPrinting_PrintText, 2544).
--define(wxHtmlEasyPrinting_PageSetup, 2545).
--define(wxHtmlEasyPrinting_SetFonts, 2546).
--define(wxHtmlEasyPrinting_SetHeader, 2547).
--define(wxHtmlEasyPrinting_SetFooter, 2548).
--define(wxGLCanvas_new_2, 2550).
--define(wxGLCanvas_new_3_1, 2551).
--define(wxGLCanvas_new_3_0, 2552).
--define(wxGLCanvas_GetContext, 2553).
--define(wxGLCanvas_SetCurrent, 2555).
--define(wxGLCanvas_SwapBuffers, 2556).
--define(wxGLCanvas_destroy, 2557).
--define(wxAuiManager_new, 2558).
--define(wxAuiManager_destruct, 2559).
--define(wxAuiManager_AddPane_2_1, 2560).
--define(wxAuiManager_AddPane_3, 2561).
--define(wxAuiManager_AddPane_2_0, 2562).
--define(wxAuiManager_DetachPane, 2563).
--define(wxAuiManager_GetAllPanes, 2564).
--define(wxAuiManager_GetArtProvider, 2565).
--define(wxAuiManager_GetDockSizeConstraint, 2566).
--define(wxAuiManager_GetFlags, 2567).
--define(wxAuiManager_GetManagedWindow, 2568).
--define(wxAuiManager_GetManager, 2569).
--define(wxAuiManager_GetPane_1_1, 2570).
--define(wxAuiManager_GetPane_1_0, 2571).
--define(wxAuiManager_HideHint, 2572).
--define(wxAuiManager_InsertPane, 2573).
--define(wxAuiManager_LoadPaneInfo, 2574).
--define(wxAuiManager_LoadPerspective, 2575).
--define(wxAuiManager_SavePaneInfo, 2576).
--define(wxAuiManager_SavePerspective, 2577).
--define(wxAuiManager_SetArtProvider, 2578).
--define(wxAuiManager_SetDockSizeConstraint, 2579).
--define(wxAuiManager_SetFlags, 2580).
--define(wxAuiManager_SetManagedWindow, 2581).
--define(wxAuiManager_ShowHint, 2582).
--define(wxAuiManager_UnInit, 2583).
--define(wxAuiManager_Update, 2584).
--define(wxAuiPaneInfo_new_0, 2585).
--define(wxAuiPaneInfo_new_1, 2586).
--define(wxAuiPaneInfo_destruct, 2587).
--define(wxAuiPaneInfo_BestSize_1, 2588).
--define(wxAuiPaneInfo_BestSize_2, 2589).
--define(wxAuiPaneInfo_Bottom, 2590).
--define(wxAuiPaneInfo_BottomDockable, 2591).
--define(wxAuiPaneInfo_Caption, 2592).
--define(wxAuiPaneInfo_CaptionVisible, 2593).
--define(wxAuiPaneInfo_Centre, 2594).
--define(wxAuiPaneInfo_CentrePane, 2595).
--define(wxAuiPaneInfo_CloseButton, 2596).
--define(wxAuiPaneInfo_DefaultPane, 2597).
--define(wxAuiPaneInfo_DestroyOnClose, 2598).
--define(wxAuiPaneInfo_Direction, 2599).
--define(wxAuiPaneInfo_Dock, 2600).
--define(wxAuiPaneInfo_Dockable, 2601).
--define(wxAuiPaneInfo_Fixed, 2602).
--define(wxAuiPaneInfo_Float, 2603).
--define(wxAuiPaneInfo_Floatable, 2604).
--define(wxAuiPaneInfo_FloatingPosition_1, 2605).
--define(wxAuiPaneInfo_FloatingPosition_2, 2606).
--define(wxAuiPaneInfo_FloatingSize_1, 2607).
--define(wxAuiPaneInfo_FloatingSize_2, 2608).
--define(wxAuiPaneInfo_Gripper, 2609).
--define(wxAuiPaneInfo_GripperTop, 2610).
--define(wxAuiPaneInfo_HasBorder, 2611).
--define(wxAuiPaneInfo_HasCaption, 2612).
--define(wxAuiPaneInfo_HasCloseButton, 2613).
--define(wxAuiPaneInfo_HasFlag, 2614).
--define(wxAuiPaneInfo_HasGripper, 2615).
--define(wxAuiPaneInfo_HasGripperTop, 2616).
--define(wxAuiPaneInfo_HasMaximizeButton, 2617).
--define(wxAuiPaneInfo_HasMinimizeButton, 2618).
--define(wxAuiPaneInfo_HasPinButton, 2619).
--define(wxAuiPaneInfo_Hide, 2620).
--define(wxAuiPaneInfo_IsBottomDockable, 2621).
--define(wxAuiPaneInfo_IsDocked, 2622).
--define(wxAuiPaneInfo_IsFixed, 2623).
--define(wxAuiPaneInfo_IsFloatable, 2624).
--define(wxAuiPaneInfo_IsFloating, 2625).
--define(wxAuiPaneInfo_IsLeftDockable, 2626).
--define(wxAuiPaneInfo_IsMovable, 2627).
--define(wxAuiPaneInfo_IsOk, 2628).
--define(wxAuiPaneInfo_IsResizable, 2629).
--define(wxAuiPaneInfo_IsRightDockable, 2630).
--define(wxAuiPaneInfo_IsShown, 2631).
--define(wxAuiPaneInfo_IsToolbar, 2632).
--define(wxAuiPaneInfo_IsTopDockable, 2633).
--define(wxAuiPaneInfo_Layer, 2634).
--define(wxAuiPaneInfo_Left, 2635).
--define(wxAuiPaneInfo_LeftDockable, 2636).
--define(wxAuiPaneInfo_MaxSize_1, 2637).
--define(wxAuiPaneInfo_MaxSize_2, 2638).
--define(wxAuiPaneInfo_MaximizeButton, 2639).
--define(wxAuiPaneInfo_MinSize_1, 2640).
--define(wxAuiPaneInfo_MinSize_2, 2641).
--define(wxAuiPaneInfo_MinimizeButton, 2642).
--define(wxAuiPaneInfo_Movable, 2643).
--define(wxAuiPaneInfo_Name, 2644).
--define(wxAuiPaneInfo_PaneBorder, 2645).
--define(wxAuiPaneInfo_PinButton, 2646).
--define(wxAuiPaneInfo_Position, 2647).
--define(wxAuiPaneInfo_Resizable, 2648).
--define(wxAuiPaneInfo_Right, 2649).
--define(wxAuiPaneInfo_RightDockable, 2650).
--define(wxAuiPaneInfo_Row, 2651).
--define(wxAuiPaneInfo_SafeSet, 2652).
--define(wxAuiPaneInfo_SetFlag, 2653).
--define(wxAuiPaneInfo_Show, 2654).
--define(wxAuiPaneInfo_ToolbarPane, 2655).
--define(wxAuiPaneInfo_Top, 2656).
--define(wxAuiPaneInfo_TopDockable, 2657).
--define(wxAuiPaneInfo_Window, 2658).
--define(wxAuiPaneInfo_GetWindow, 2659).
--define(wxAuiPaneInfo_GetFrame, 2660).
--define(wxAuiPaneInfo_GetDirection, 2661).
--define(wxAuiPaneInfo_GetLayer, 2662).
--define(wxAuiPaneInfo_GetRow, 2663).
--define(wxAuiPaneInfo_GetPosition, 2664).
--define(wxAuiPaneInfo_GetFloatingPosition, 2665).
--define(wxAuiPaneInfo_GetFloatingSize, 2666).
--define(wxAuiNotebook_new_0, 2667).
--define(wxAuiNotebook_new_2, 2668).
--define(wxAuiNotebook_AddPage, 2669).
--define(wxAuiNotebook_Create, 2670).
--define(wxAuiNotebook_DeletePage, 2671).
--define(wxAuiNotebook_GetArtProvider, 2672).
--define(wxAuiNotebook_GetPage, 2673).
--define(wxAuiNotebook_GetPageBitmap, 2674).
--define(wxAuiNotebook_GetPageCount, 2675).
--define(wxAuiNotebook_GetPageIndex, 2676).
--define(wxAuiNotebook_GetPageText, 2677).
--define(wxAuiNotebook_GetSelection, 2678).
--define(wxAuiNotebook_InsertPage, 2679).
--define(wxAuiNotebook_RemovePage, 2680).
--define(wxAuiNotebook_SetArtProvider, 2681).
--define(wxAuiNotebook_SetFont, 2682).
--define(wxAuiNotebook_SetPageBitmap, 2683).
--define(wxAuiNotebook_SetPageText, 2684).
--define(wxAuiNotebook_SetSelection, 2685).
--define(wxAuiNotebook_SetTabCtrlHeight, 2686).
--define(wxAuiNotebook_SetUniformBitmapSize, 2687).
--define(wxAuiNotebook_destroy, 2688).
--define(wxAuiTabArt_SetFlags, 2689).
--define(wxAuiTabArt_SetMeasuringFont, 2690).
--define(wxAuiTabArt_SetNormalFont, 2691).
--define(wxAuiTabArt_SetSelectedFont, 2692).
--define(wxAuiTabArt_SetColour, 2693).
--define(wxAuiTabArt_SetActiveColour, 2694).
--define(wxAuiDockArt_GetColour, 2695).
--define(wxAuiDockArt_GetFont, 2696).
--define(wxAuiDockArt_GetMetric, 2697).
--define(wxAuiDockArt_SetColour, 2698).
--define(wxAuiDockArt_SetFont, 2699).
--define(wxAuiDockArt_SetMetric, 2700).
--define(wxAuiSimpleTabArt_new, 2701).
--define(wxAuiSimpleTabArt_destroy, 2702).
--define(wxMDIParentFrame_new_0, 2703).
--define(wxMDIParentFrame_new_4, 2704).
--define(wxMDIParentFrame_destruct, 2705).
--define(wxMDIParentFrame_ActivateNext, 2706).
--define(wxMDIParentFrame_ActivatePrevious, 2707).
--define(wxMDIParentFrame_ArrangeIcons, 2708).
--define(wxMDIParentFrame_Cascade, 2709).
--define(wxMDIParentFrame_Create, 2710).
--define(wxMDIParentFrame_GetActiveChild, 2711).
--define(wxMDIParentFrame_GetClientWindow, 2712).
--define(wxMDIParentFrame_Tile, 2713).
--define(wxMDIChildFrame_new_0, 2714).
--define(wxMDIChildFrame_new_4, 2715).
--define(wxMDIChildFrame_destruct, 2716).
--define(wxMDIChildFrame_Activate, 2717).
--define(wxMDIChildFrame_Create, 2718).
--define(wxMDIChildFrame_Maximize, 2719).
--define(wxMDIChildFrame_Restore, 2720).
--define(wxMDIClientWindow_new_0, 2721).
--define(wxMDIClientWindow_new_2, 2722).
--define(wxMDIClientWindow_destruct, 2723).
--define(wxMDIClientWindow_CreateClient, 2724).
--define(wxLayoutAlgorithm_new, 2725).
--define(wxLayoutAlgorithm_LayoutFrame, 2726).
--define(wxLayoutAlgorithm_LayoutMDIFrame, 2727).
--define(wxLayoutAlgorithm_LayoutWindow, 2728).
--define(wxLayoutAlgorithm_destroy, 2729).
--define(wxEvent_GetId, 2730).
--define(wxEvent_GetSkipped, 2731).
--define(wxEvent_GetTimestamp, 2732).
--define(wxEvent_IsCommandEvent, 2733).
--define(wxEvent_ResumePropagation, 2734).
--define(wxEvent_ShouldPropagate, 2735).
--define(wxEvent_Skip, 2736).
--define(wxEvent_StopPropagation, 2737).
--define(wxCommandEvent_getClientData, 2738).
--define(wxCommandEvent_GetExtraLong, 2739).
--define(wxCommandEvent_GetInt, 2740).
--define(wxCommandEvent_GetSelection, 2741).
--define(wxCommandEvent_GetString, 2742).
--define(wxCommandEvent_IsChecked, 2743).
--define(wxCommandEvent_IsSelection, 2744).
--define(wxCommandEvent_SetInt, 2745).
--define(wxCommandEvent_SetString, 2746).
--define(wxScrollEvent_GetOrientation, 2747).
--define(wxScrollEvent_GetPosition, 2748).
--define(wxScrollWinEvent_GetOrientation, 2749).
--define(wxScrollWinEvent_GetPosition, 2750).
--define(wxMouseEvent_AltDown, 2751).
--define(wxMouseEvent_Button, 2752).
--define(wxMouseEvent_ButtonDClick, 2753).
--define(wxMouseEvent_ButtonDown, 2754).
--define(wxMouseEvent_ButtonUp, 2755).
--define(wxMouseEvent_CmdDown, 2756).
--define(wxMouseEvent_ControlDown, 2757).
--define(wxMouseEvent_Dragging, 2758).
--define(wxMouseEvent_Entering, 2759).
--define(wxMouseEvent_GetButton, 2760).
--define(wxMouseEvent_GetPosition, 2763).
--define(wxMouseEvent_GetLogicalPosition, 2764).
--define(wxMouseEvent_GetLinesPerAction, 2765).
--define(wxMouseEvent_GetWheelRotation, 2766).
--define(wxMouseEvent_GetWheelDelta, 2767).
--define(wxMouseEvent_GetX, 2768).
--define(wxMouseEvent_GetY, 2769).
--define(wxMouseEvent_IsButton, 2770).
--define(wxMouseEvent_IsPageScroll, 2771).
--define(wxMouseEvent_Leaving, 2772).
--define(wxMouseEvent_LeftDClick, 2773).
--define(wxMouseEvent_LeftDown, 2774).
--define(wxMouseEvent_LeftIsDown, 2775).
--define(wxMouseEvent_LeftUp, 2776).
--define(wxMouseEvent_MetaDown, 2777).
--define(wxMouseEvent_MiddleDClick, 2778).
--define(wxMouseEvent_MiddleDown, 2779).
--define(wxMouseEvent_MiddleIsDown, 2780).
--define(wxMouseEvent_MiddleUp, 2781).
--define(wxMouseEvent_Moving, 2782).
--define(wxMouseEvent_RightDClick, 2783).
--define(wxMouseEvent_RightDown, 2784).
--define(wxMouseEvent_RightIsDown, 2785).
--define(wxMouseEvent_RightUp, 2786).
--define(wxMouseEvent_ShiftDown, 2787).
--define(wxSetCursorEvent_GetCursor, 2788).
--define(wxSetCursorEvent_GetX, 2789).
--define(wxSetCursorEvent_GetY, 2790).
--define(wxSetCursorEvent_HasCursor, 2791).
--define(wxSetCursorEvent_SetCursor, 2792).
--define(wxKeyEvent_AltDown, 2793).
--define(wxKeyEvent_CmdDown, 2794).
--define(wxKeyEvent_ControlDown, 2795).
--define(wxKeyEvent_GetKeyCode, 2796).
--define(wxKeyEvent_GetModifiers, 2797).
--define(wxKeyEvent_GetPosition, 2800).
--define(wxKeyEvent_GetRawKeyCode, 2801).
--define(wxKeyEvent_GetRawKeyFlags, 2802).
--define(wxKeyEvent_GetUnicodeKey, 2803).
--define(wxKeyEvent_GetX, 2804).
--define(wxKeyEvent_GetY, 2805).
--define(wxKeyEvent_HasModifiers, 2806).
--define(wxKeyEvent_MetaDown, 2807).
--define(wxKeyEvent_ShiftDown, 2808).
--define(wxSizeEvent_GetSize, 2809).
--define(wxMoveEvent_GetPosition, 2810).
--define(wxEraseEvent_GetDC, 2811).
--define(wxFocusEvent_GetWindow, 2812).
--define(wxChildFocusEvent_GetWindow, 2813).
--define(wxMenuEvent_GetMenu, 2814).
--define(wxMenuEvent_GetMenuId, 2815).
--define(wxMenuEvent_IsPopup, 2816).
--define(wxCloseEvent_CanVeto, 2817).
--define(wxCloseEvent_GetLoggingOff, 2818).
--define(wxCloseEvent_SetCanVeto, 2819).
--define(wxCloseEvent_SetLoggingOff, 2820).
--define(wxCloseEvent_Veto, 2821).
--define(wxShowEvent_SetShow, 2822).
--define(wxShowEvent_GetShow, 2823).
--define(wxIconizeEvent_Iconized, 2824).
--define(wxJoystickEvent_ButtonDown, 2825).
--define(wxJoystickEvent_ButtonIsDown, 2826).
--define(wxJoystickEvent_ButtonUp, 2827).
--define(wxJoystickEvent_GetButtonChange, 2828).
--define(wxJoystickEvent_GetButtonState, 2829).
--define(wxJoystickEvent_GetJoystick, 2830).
--define(wxJoystickEvent_GetPosition, 2831).
--define(wxJoystickEvent_GetZPosition, 2832).
--define(wxJoystickEvent_IsButton, 2833).
--define(wxJoystickEvent_IsMove, 2834).
--define(wxJoystickEvent_IsZMove, 2835).
--define(wxUpdateUIEvent_CanUpdate, 2836).
--define(wxUpdateUIEvent_Check, 2837).
--define(wxUpdateUIEvent_Enable, 2838).
--define(wxUpdateUIEvent_Show, 2839).
--define(wxUpdateUIEvent_GetChecked, 2840).
--define(wxUpdateUIEvent_GetEnabled, 2841).
--define(wxUpdateUIEvent_GetShown, 2842).
--define(wxUpdateUIEvent_GetSetChecked, 2843).
--define(wxUpdateUIEvent_GetSetEnabled, 2844).
--define(wxUpdateUIEvent_GetSetShown, 2845).
--define(wxUpdateUIEvent_GetSetText, 2846).
--define(wxUpdateUIEvent_GetText, 2847).
--define(wxUpdateUIEvent_GetMode, 2848).
--define(wxUpdateUIEvent_GetUpdateInterval, 2849).
--define(wxUpdateUIEvent_ResetUpdateTime, 2850).
--define(wxUpdateUIEvent_SetMode, 2851).
--define(wxUpdateUIEvent_SetText, 2852).
--define(wxUpdateUIEvent_SetUpdateInterval, 2853).
--define(wxMouseCaptureChangedEvent_GetCapturedWindow, 2854).
--define(wxPaletteChangedEvent_SetChangedWindow, 2855).
--define(wxPaletteChangedEvent_GetChangedWindow, 2856).
--define(wxQueryNewPaletteEvent_SetPaletteRealized, 2857).
--define(wxQueryNewPaletteEvent_GetPaletteRealized, 2858).
--define(wxNavigationKeyEvent_GetDirection, 2859).
--define(wxNavigationKeyEvent_SetDirection, 2860).
--define(wxNavigationKeyEvent_IsWindowChange, 2861).
--define(wxNavigationKeyEvent_SetWindowChange, 2862).
--define(wxNavigationKeyEvent_IsFromTab, 2863).
--define(wxNavigationKeyEvent_SetFromTab, 2864).
--define(wxNavigationKeyEvent_GetCurrentFocus, 2865).
--define(wxNavigationKeyEvent_SetCurrentFocus, 2866).
--define(wxHelpEvent_GetOrigin, 2867).
--define(wxHelpEvent_GetPosition, 2868).
--define(wxHelpEvent_SetOrigin, 2869).
--define(wxHelpEvent_SetPosition, 2870).
--define(wxContextMenuEvent_GetPosition, 2871).
--define(wxContextMenuEvent_SetPosition, 2872).
--define(wxIdleEvent_CanSend, 2873).
--define(wxIdleEvent_GetMode, 2874).
--define(wxIdleEvent_RequestMore, 2875).
--define(wxIdleEvent_MoreRequested, 2876).
--define(wxIdleEvent_SetMode, 2877).
--define(wxGridEvent_AltDown, 2878).
--define(wxGridEvent_ControlDown, 2879).
--define(wxGridEvent_GetCol, 2880).
--define(wxGridEvent_GetPosition, 2881).
--define(wxGridEvent_GetRow, 2882).
--define(wxGridEvent_MetaDown, 2883).
--define(wxGridEvent_Selecting, 2884).
--define(wxGridEvent_ShiftDown, 2885).
--define(wxNotifyEvent_Allow, 2886).
--define(wxNotifyEvent_IsAllowed, 2887).
--define(wxNotifyEvent_Veto, 2888).
--define(wxSashEvent_GetEdge, 2889).
--define(wxSashEvent_GetDragRect, 2890).
--define(wxSashEvent_GetDragStatus, 2891).
--define(wxListEvent_GetCacheFrom, 2892).
--define(wxListEvent_GetCacheTo, 2893).
--define(wxListEvent_GetKeyCode, 2894).
--define(wxListEvent_GetIndex, 2895).
--define(wxListEvent_GetColumn, 2896).
--define(wxListEvent_GetPoint, 2897).
--define(wxListEvent_GetLabel, 2898).
--define(wxListEvent_GetText, 2899).
--define(wxListEvent_GetImage, 2900).
--define(wxListEvent_GetData, 2901).
--define(wxListEvent_GetMask, 2902).
--define(wxListEvent_GetItem, 2903).
--define(wxListEvent_IsEditCancelled, 2904).
--define(wxDateEvent_GetDate, 2905).
--define(wxCalendarEvent_GetWeekDay, 2906).
--define(wxFileDirPickerEvent_GetPath, 2907).
--define(wxColourPickerEvent_GetColour, 2908).
--define(wxFontPickerEvent_GetFont, 2909).
--define(wxStyledTextEvent_GetPosition, 2910).
--define(wxStyledTextEvent_GetKey, 2911).
--define(wxStyledTextEvent_GetModifiers, 2912).
--define(wxStyledTextEvent_GetModificationType, 2913).
--define(wxStyledTextEvent_GetText, 2914).
--define(wxStyledTextEvent_GetLength, 2915).
--define(wxStyledTextEvent_GetLinesAdded, 2916).
--define(wxStyledTextEvent_GetLine, 2917).
--define(wxStyledTextEvent_GetFoldLevelNow, 2918).
--define(wxStyledTextEvent_GetFoldLevelPrev, 2919).
--define(wxStyledTextEvent_GetMargin, 2920).
--define(wxStyledTextEvent_GetMessage, 2921).
--define(wxStyledTextEvent_GetWParam, 2922).
--define(wxStyledTextEvent_GetLParam, 2923).
--define(wxStyledTextEvent_GetListType, 2924).
--define(wxStyledTextEvent_GetX, 2925).
--define(wxStyledTextEvent_GetY, 2926).
--define(wxStyledTextEvent_GetDragText, 2927).
--define(wxStyledTextEvent_GetDragAllowMove, 2928).
--define(wxStyledTextEvent_GetDragResult, 2929).
--define(wxStyledTextEvent_GetShift, 2930).
--define(wxStyledTextEvent_GetControl, 2931).
--define(wxStyledTextEvent_GetAlt, 2932).
--define(utils_wxGetKeyState, 2933).
--define(utils_wxGetMousePosition, 2934).
--define(utils_wxGetMouseState, 2935).
--define(utils_wxSetDetectableAutoRepeat, 2936).
--define(utils_wxBell, 2937).
--define(utils_wxFindMenuItemId, 2938).
--define(utils_wxGenericFindWindowAtPoint, 2939).
--define(utils_wxFindWindowAtPoint, 2940).
--define(utils_wxBeginBusyCursor, 2941).
--define(utils_wxEndBusyCursor, 2942).
--define(utils_wxIsBusy, 2943).
--define(utils_wxShutdown, 2944).
--define(utils_wxShell, 2945).
--define(utils_wxLaunchDefaultBrowser, 2946).
--define(utils_wxGetEmailAddress, 2947).
--define(utils_wxGetUserId, 2948).
--define(utils_wxGetHomeDir, 2949).
--define(utils_wxNewId, 2950).
--define(utils_wxRegisterId, 2951).
--define(utils_wxGetCurrentId, 2952).
--define(utils_wxGetOsDescription, 2953).
--define(utils_wxIsPlatformLittleEndian, 2954).
--define(utils_wxIsPlatform64Bit, 2955).
--define(gdicmn_wxDisplaySize, 2956).
--define(gdicmn_wxSetCursor, 2957).
--define(wxPrintout_new, 2958).
--define(wxPrintout_destruct, 2959).
--define(wxPrintout_GetDC, 2960).
--define(wxPrintout_GetPageSizeMM, 2961).
--define(wxPrintout_GetPageSizePixels, 2962).
--define(wxPrintout_GetPaperRectPixels, 2963).
--define(wxPrintout_GetPPIPrinter, 2964).
--define(wxPrintout_GetPPIScreen, 2965).
--define(wxPrintout_GetTitle, 2966).
--define(wxPrintout_IsPreview, 2967).
--define(wxPrintout_FitThisSizeToPaper, 2968).
--define(wxPrintout_FitThisSizeToPage, 2969).
--define(wxPrintout_FitThisSizeToPageMargins, 2970).
--define(wxPrintout_MapScreenSizeToPaper, 2971).
--define(wxPrintout_MapScreenSizeToPage, 2972).
--define(wxPrintout_MapScreenSizeToPageMargins, 2973).
--define(wxPrintout_MapScreenSizeToDevice, 2974).
--define(wxPrintout_GetLogicalPaperRect, 2975).
--define(wxPrintout_GetLogicalPageRect, 2976).
--define(wxPrintout_GetLogicalPageMarginsRect, 2977).
--define(wxPrintout_SetLogicalOrigin, 2978).
--define(wxPrintout_OffsetLogicalOrigin, 2979).
--define(wxStyledTextCtrl_new_2, 2980).
--define(wxStyledTextCtrl_new_0, 2981).
--define(wxStyledTextCtrl_destruct, 2982).
--define(wxStyledTextCtrl_Create, 2983).
--define(wxStyledTextCtrl_AddText, 2984).
--define(wxStyledTextCtrl_AddStyledText, 2985).
--define(wxStyledTextCtrl_InsertText, 2986).
--define(wxStyledTextCtrl_ClearAll, 2987).
--define(wxStyledTextCtrl_ClearDocumentStyle, 2988).
--define(wxStyledTextCtrl_GetLength, 2989).
--define(wxStyledTextCtrl_GetCharAt, 2990).
--define(wxStyledTextCtrl_GetCurrentPos, 2991).
--define(wxStyledTextCtrl_GetAnchor, 2992).
--define(wxStyledTextCtrl_GetStyleAt, 2993).
--define(wxStyledTextCtrl_Redo, 2994).
--define(wxStyledTextCtrl_SetUndoCollection, 2995).
--define(wxStyledTextCtrl_SelectAll, 2996).
--define(wxStyledTextCtrl_SetSavePoint, 2997).
--define(wxStyledTextCtrl_GetStyledText, 2998).
--define(wxStyledTextCtrl_CanRedo, 2999).
--define(wxStyledTextCtrl_MarkerLineFromHandle, 3000).
--define(wxStyledTextCtrl_MarkerDeleteHandle, 3001).
--define(wxStyledTextCtrl_GetUndoCollection, 3002).
--define(wxStyledTextCtrl_GetViewWhiteSpace, 3003).
--define(wxStyledTextCtrl_SetViewWhiteSpace, 3004).
--define(wxStyledTextCtrl_PositionFromPoint, 3005).
--define(wxStyledTextCtrl_PositionFromPointClose, 3006).
--define(wxStyledTextCtrl_GotoLine, 3007).
--define(wxStyledTextCtrl_GotoPos, 3008).
--define(wxStyledTextCtrl_SetAnchor, 3009).
--define(wxStyledTextCtrl_GetCurLine, 3010).
--define(wxStyledTextCtrl_GetEndStyled, 3011).
--define(wxStyledTextCtrl_ConvertEOLs, 3012).
--define(wxStyledTextCtrl_GetEOLMode, 3013).
--define(wxStyledTextCtrl_SetEOLMode, 3014).
--define(wxStyledTextCtrl_StartStyling, 3015).
--define(wxStyledTextCtrl_SetStyling, 3016).
--define(wxStyledTextCtrl_GetBufferedDraw, 3017).
--define(wxStyledTextCtrl_SetBufferedDraw, 3018).
--define(wxStyledTextCtrl_SetTabWidth, 3019).
--define(wxStyledTextCtrl_GetTabWidth, 3020).
--define(wxStyledTextCtrl_SetCodePage, 3021).
--define(wxStyledTextCtrl_MarkerDefine, 3022).
--define(wxStyledTextCtrl_MarkerSetForeground, 3023).
--define(wxStyledTextCtrl_MarkerSetBackground, 3024).
--define(wxStyledTextCtrl_MarkerAdd, 3025).
--define(wxStyledTextCtrl_MarkerDelete, 3026).
--define(wxStyledTextCtrl_MarkerDeleteAll, 3027).
--define(wxStyledTextCtrl_MarkerGet, 3028).
--define(wxStyledTextCtrl_MarkerNext, 3029).
--define(wxStyledTextCtrl_MarkerPrevious, 3030).
--define(wxStyledTextCtrl_MarkerDefineBitmap, 3031).
--define(wxStyledTextCtrl_MarkerAddSet, 3032).
--define(wxStyledTextCtrl_MarkerSetAlpha, 3033).
--define(wxStyledTextCtrl_SetMarginType, 3034).
--define(wxStyledTextCtrl_GetMarginType, 3035).
--define(wxStyledTextCtrl_SetMarginWidth, 3036).
--define(wxStyledTextCtrl_GetMarginWidth, 3037).
--define(wxStyledTextCtrl_SetMarginMask, 3038).
--define(wxStyledTextCtrl_GetMarginMask, 3039).
--define(wxStyledTextCtrl_SetMarginSensitive, 3040).
--define(wxStyledTextCtrl_GetMarginSensitive, 3041).
--define(wxStyledTextCtrl_StyleClearAll, 3042).
--define(wxStyledTextCtrl_StyleSetForeground, 3043).
--define(wxStyledTextCtrl_StyleSetBackground, 3044).
--define(wxStyledTextCtrl_StyleSetBold, 3045).
--define(wxStyledTextCtrl_StyleSetItalic, 3046).
--define(wxStyledTextCtrl_StyleSetSize, 3047).
--define(wxStyledTextCtrl_StyleSetFaceName, 3048).
--define(wxStyledTextCtrl_StyleSetEOLFilled, 3049).
--define(wxStyledTextCtrl_StyleResetDefault, 3050).
--define(wxStyledTextCtrl_StyleSetUnderline, 3051).
--define(wxStyledTextCtrl_StyleSetCase, 3052).
--define(wxStyledTextCtrl_StyleSetHotSpot, 3053).
--define(wxStyledTextCtrl_SetSelForeground, 3054).
--define(wxStyledTextCtrl_SetSelBackground, 3055).
--define(wxStyledTextCtrl_GetSelAlpha, 3056).
--define(wxStyledTextCtrl_SetSelAlpha, 3057).
--define(wxStyledTextCtrl_SetCaretForeground, 3058).
--define(wxStyledTextCtrl_CmdKeyAssign, 3059).
--define(wxStyledTextCtrl_CmdKeyClear, 3060).
--define(wxStyledTextCtrl_CmdKeyClearAll, 3061).
--define(wxStyledTextCtrl_SetStyleBytes, 3062).
--define(wxStyledTextCtrl_StyleSetVisible, 3063).
--define(wxStyledTextCtrl_GetCaretPeriod, 3064).
--define(wxStyledTextCtrl_SetCaretPeriod, 3065).
--define(wxStyledTextCtrl_SetWordChars, 3066).
--define(wxStyledTextCtrl_BeginUndoAction, 3067).
--define(wxStyledTextCtrl_EndUndoAction, 3068).
--define(wxStyledTextCtrl_IndicatorSetStyle, 3069).
--define(wxStyledTextCtrl_IndicatorGetStyle, 3070).
--define(wxStyledTextCtrl_IndicatorSetForeground, 3071).
--define(wxStyledTextCtrl_IndicatorGetForeground, 3072).
--define(wxStyledTextCtrl_SetWhitespaceForeground, 3073).
--define(wxStyledTextCtrl_SetWhitespaceBackground, 3074).
--define(wxStyledTextCtrl_GetStyleBits, 3075).
--define(wxStyledTextCtrl_SetLineState, 3076).
--define(wxStyledTextCtrl_GetLineState, 3077).
--define(wxStyledTextCtrl_GetMaxLineState, 3078).
--define(wxStyledTextCtrl_GetCaretLineVisible, 3079).
--define(wxStyledTextCtrl_SetCaretLineVisible, 3080).
--define(wxStyledTextCtrl_GetCaretLineBackground, 3081).
--define(wxStyledTextCtrl_SetCaretLineBackground, 3082).
--define(wxStyledTextCtrl_AutoCompShow, 3083).
--define(wxStyledTextCtrl_AutoCompCancel, 3084).
--define(wxStyledTextCtrl_AutoCompActive, 3085).
--define(wxStyledTextCtrl_AutoCompPosStart, 3086).
--define(wxStyledTextCtrl_AutoCompComplete, 3087).
--define(wxStyledTextCtrl_AutoCompStops, 3088).
--define(wxStyledTextCtrl_AutoCompSetSeparator, 3089).
--define(wxStyledTextCtrl_AutoCompGetSeparator, 3090).
--define(wxStyledTextCtrl_AutoCompSelect, 3091).
--define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3092).
--define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3093).
--define(wxStyledTextCtrl_AutoCompSetFillUps, 3094).
--define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3095).
--define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3096).
--define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3097).
--define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3098).
--define(wxStyledTextCtrl_UserListShow, 3099).
--define(wxStyledTextCtrl_AutoCompSetAutoHide, 3100).
--define(wxStyledTextCtrl_AutoCompGetAutoHide, 3101).
--define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3102).
--define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3103).
--define(wxStyledTextCtrl_RegisterImage, 3104).
--define(wxStyledTextCtrl_ClearRegisteredImages, 3105).
--define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3106).
--define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3107).
--define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3108).
--define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3109).
--define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3110).
--define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3111).
--define(wxStyledTextCtrl_SetIndent, 3112).
--define(wxStyledTextCtrl_GetIndent, 3113).
--define(wxStyledTextCtrl_SetUseTabs, 3114).
--define(wxStyledTextCtrl_GetUseTabs, 3115).
--define(wxStyledTextCtrl_SetLineIndentation, 3116).
--define(wxStyledTextCtrl_GetLineIndentation, 3117).
--define(wxStyledTextCtrl_GetLineIndentPosition, 3118).
--define(wxStyledTextCtrl_GetColumn, 3119).
--define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3120).
--define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3121).
--define(wxStyledTextCtrl_SetIndentationGuides, 3122).
--define(wxStyledTextCtrl_GetIndentationGuides, 3123).
--define(wxStyledTextCtrl_SetHighlightGuide, 3124).
--define(wxStyledTextCtrl_GetHighlightGuide, 3125).
--define(wxStyledTextCtrl_GetLineEndPosition, 3126).
--define(wxStyledTextCtrl_GetCodePage, 3127).
--define(wxStyledTextCtrl_GetCaretForeground, 3128).
--define(wxStyledTextCtrl_GetReadOnly, 3129).
--define(wxStyledTextCtrl_SetCurrentPos, 3130).
--define(wxStyledTextCtrl_SetSelectionStart, 3131).
--define(wxStyledTextCtrl_GetSelectionStart, 3132).
--define(wxStyledTextCtrl_SetSelectionEnd, 3133).
--define(wxStyledTextCtrl_GetSelectionEnd, 3134).
--define(wxStyledTextCtrl_SetPrintMagnification, 3135).
--define(wxStyledTextCtrl_GetPrintMagnification, 3136).
--define(wxStyledTextCtrl_SetPrintColourMode, 3137).
--define(wxStyledTextCtrl_GetPrintColourMode, 3138).
--define(wxStyledTextCtrl_FindText, 3139).
--define(wxStyledTextCtrl_FormatRange, 3140).
--define(wxStyledTextCtrl_GetFirstVisibleLine, 3141).
--define(wxStyledTextCtrl_GetLine, 3142).
--define(wxStyledTextCtrl_GetLineCount, 3143).
--define(wxStyledTextCtrl_SetMarginLeft, 3144).
--define(wxStyledTextCtrl_GetMarginLeft, 3145).
--define(wxStyledTextCtrl_SetMarginRight, 3146).
--define(wxStyledTextCtrl_GetMarginRight, 3147).
--define(wxStyledTextCtrl_GetModify, 3148).
--define(wxStyledTextCtrl_SetSelection, 3149).
--define(wxStyledTextCtrl_GetSelectedText, 3150).
--define(wxStyledTextCtrl_GetTextRange, 3151).
--define(wxStyledTextCtrl_HideSelection, 3152).
--define(wxStyledTextCtrl_LineFromPosition, 3153).
--define(wxStyledTextCtrl_PositionFromLine, 3154).
--define(wxStyledTextCtrl_LineScroll, 3155).
--define(wxStyledTextCtrl_EnsureCaretVisible, 3156).
--define(wxStyledTextCtrl_ReplaceSelection, 3157).
--define(wxStyledTextCtrl_SetReadOnly, 3158).
--define(wxStyledTextCtrl_CanPaste, 3159).
--define(wxStyledTextCtrl_CanUndo, 3160).
--define(wxStyledTextCtrl_EmptyUndoBuffer, 3161).
--define(wxStyledTextCtrl_Undo, 3162).
--define(wxStyledTextCtrl_Cut, 3163).
--define(wxStyledTextCtrl_Copy, 3164).
--define(wxStyledTextCtrl_Paste, 3165).
--define(wxStyledTextCtrl_Clear, 3166).
--define(wxStyledTextCtrl_SetText, 3167).
--define(wxStyledTextCtrl_GetText, 3168).
--define(wxStyledTextCtrl_GetTextLength, 3169).
--define(wxStyledTextCtrl_GetOvertype, 3170).
--define(wxStyledTextCtrl_SetCaretWidth, 3171).
--define(wxStyledTextCtrl_GetCaretWidth, 3172).
--define(wxStyledTextCtrl_SetTargetStart, 3173).
--define(wxStyledTextCtrl_GetTargetStart, 3174).
--define(wxStyledTextCtrl_SetTargetEnd, 3175).
--define(wxStyledTextCtrl_GetTargetEnd, 3176).
--define(wxStyledTextCtrl_ReplaceTarget, 3177).
--define(wxStyledTextCtrl_SearchInTarget, 3178).
--define(wxStyledTextCtrl_SetSearchFlags, 3179).
--define(wxStyledTextCtrl_GetSearchFlags, 3180).
--define(wxStyledTextCtrl_CallTipShow, 3181).
--define(wxStyledTextCtrl_CallTipCancel, 3182).
--define(wxStyledTextCtrl_CallTipActive, 3183).
--define(wxStyledTextCtrl_CallTipPosAtStart, 3184).
--define(wxStyledTextCtrl_CallTipSetHighlight, 3185).
--define(wxStyledTextCtrl_CallTipSetBackground, 3186).
--define(wxStyledTextCtrl_CallTipSetForeground, 3187).
--define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3188).
--define(wxStyledTextCtrl_CallTipUseStyle, 3189).
--define(wxStyledTextCtrl_VisibleFromDocLine, 3190).
--define(wxStyledTextCtrl_DocLineFromVisible, 3191).
--define(wxStyledTextCtrl_WrapCount, 3192).
--define(wxStyledTextCtrl_SetFoldLevel, 3193).
--define(wxStyledTextCtrl_GetFoldLevel, 3194).
--define(wxStyledTextCtrl_GetLastChild, 3195).
--define(wxStyledTextCtrl_GetFoldParent, 3196).
--define(wxStyledTextCtrl_ShowLines, 3197).
--define(wxStyledTextCtrl_HideLines, 3198).
--define(wxStyledTextCtrl_GetLineVisible, 3199).
--define(wxStyledTextCtrl_SetFoldExpanded, 3200).
--define(wxStyledTextCtrl_GetFoldExpanded, 3201).
--define(wxStyledTextCtrl_ToggleFold, 3202).
--define(wxStyledTextCtrl_EnsureVisible, 3203).
--define(wxStyledTextCtrl_SetFoldFlags, 3204).
--define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3205).
--define(wxStyledTextCtrl_SetTabIndents, 3206).
--define(wxStyledTextCtrl_GetTabIndents, 3207).
--define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3208).
--define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3209).
--define(wxStyledTextCtrl_SetMouseDwellTime, 3210).
--define(wxStyledTextCtrl_GetMouseDwellTime, 3211).
--define(wxStyledTextCtrl_WordStartPosition, 3212).
--define(wxStyledTextCtrl_WordEndPosition, 3213).
--define(wxStyledTextCtrl_SetWrapMode, 3214).
--define(wxStyledTextCtrl_GetWrapMode, 3215).
--define(wxStyledTextCtrl_SetWrapVisualFlags, 3216).
--define(wxStyledTextCtrl_GetWrapVisualFlags, 3217).
--define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3218).
--define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3219).
--define(wxStyledTextCtrl_SetWrapStartIndent, 3220).
--define(wxStyledTextCtrl_GetWrapStartIndent, 3221).
--define(wxStyledTextCtrl_SetLayoutCache, 3222).
--define(wxStyledTextCtrl_GetLayoutCache, 3223).
--define(wxStyledTextCtrl_SetScrollWidth, 3224).
--define(wxStyledTextCtrl_GetScrollWidth, 3225).
--define(wxStyledTextCtrl_TextWidth, 3226).
--define(wxStyledTextCtrl_GetEndAtLastLine, 3227).
--define(wxStyledTextCtrl_TextHeight, 3228).
--define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3229).
--define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3230).
--define(wxStyledTextCtrl_AppendText, 3231).
--define(wxStyledTextCtrl_GetTwoPhaseDraw, 3232).
--define(wxStyledTextCtrl_SetTwoPhaseDraw, 3233).
--define(wxStyledTextCtrl_TargetFromSelection, 3234).
--define(wxStyledTextCtrl_LinesJoin, 3235).
--define(wxStyledTextCtrl_LinesSplit, 3236).
--define(wxStyledTextCtrl_SetFoldMarginColour, 3237).
--define(wxStyledTextCtrl_SetFoldMarginHiColour, 3238).
--define(wxStyledTextCtrl_LineDown, 3239).
--define(wxStyledTextCtrl_LineDownExtend, 3240).
--define(wxStyledTextCtrl_LineUp, 3241).
--define(wxStyledTextCtrl_LineUpExtend, 3242).
--define(wxStyledTextCtrl_CharLeft, 3243).
--define(wxStyledTextCtrl_CharLeftExtend, 3244).
--define(wxStyledTextCtrl_CharRight, 3245).
--define(wxStyledTextCtrl_CharRightExtend, 3246).
--define(wxStyledTextCtrl_WordLeft, 3247).
--define(wxStyledTextCtrl_WordLeftExtend, 3248).
--define(wxStyledTextCtrl_WordRight, 3249).
--define(wxStyledTextCtrl_WordRightExtend, 3250).
--define(wxStyledTextCtrl_Home, 3251).
--define(wxStyledTextCtrl_HomeExtend, 3252).
--define(wxStyledTextCtrl_LineEnd, 3253).
--define(wxStyledTextCtrl_LineEndExtend, 3254).
--define(wxStyledTextCtrl_DocumentStart, 3255).
--define(wxStyledTextCtrl_DocumentStartExtend, 3256).
--define(wxStyledTextCtrl_DocumentEnd, 3257).
--define(wxStyledTextCtrl_DocumentEndExtend, 3258).
--define(wxStyledTextCtrl_PageUp, 3259).
--define(wxStyledTextCtrl_PageUpExtend, 3260).
--define(wxStyledTextCtrl_PageDown, 3261).
--define(wxStyledTextCtrl_PageDownExtend, 3262).
--define(wxStyledTextCtrl_EditToggleOvertype, 3263).
--define(wxStyledTextCtrl_Cancel, 3264).
--define(wxStyledTextCtrl_DeleteBack, 3265).
--define(wxStyledTextCtrl_Tab, 3266).
--define(wxStyledTextCtrl_BackTab, 3267).
--define(wxStyledTextCtrl_NewLine, 3268).
--define(wxStyledTextCtrl_FormFeed, 3269).
--define(wxStyledTextCtrl_VCHome, 3270).
--define(wxStyledTextCtrl_VCHomeExtend, 3271).
--define(wxStyledTextCtrl_ZoomIn, 3272).
--define(wxStyledTextCtrl_ZoomOut, 3273).
--define(wxStyledTextCtrl_DelWordLeft, 3274).
--define(wxStyledTextCtrl_DelWordRight, 3275).
--define(wxStyledTextCtrl_LineCut, 3276).
--define(wxStyledTextCtrl_LineDelete, 3277).
--define(wxStyledTextCtrl_LineTranspose, 3278).
--define(wxStyledTextCtrl_LineDuplicate, 3279).
--define(wxStyledTextCtrl_LowerCase, 3280).
--define(wxStyledTextCtrl_UpperCase, 3281).
--define(wxStyledTextCtrl_LineScrollDown, 3282).
--define(wxStyledTextCtrl_LineScrollUp, 3283).
--define(wxStyledTextCtrl_DeleteBackNotLine, 3284).
--define(wxStyledTextCtrl_HomeDisplay, 3285).
--define(wxStyledTextCtrl_HomeDisplayExtend, 3286).
--define(wxStyledTextCtrl_LineEndDisplay, 3287).
--define(wxStyledTextCtrl_LineEndDisplayExtend, 3288).
--define(wxStyledTextCtrl_HomeWrapExtend, 3289).
--define(wxStyledTextCtrl_LineEndWrap, 3290).
--define(wxStyledTextCtrl_LineEndWrapExtend, 3291).
--define(wxStyledTextCtrl_VCHomeWrap, 3292).
--define(wxStyledTextCtrl_VCHomeWrapExtend, 3293).
--define(wxStyledTextCtrl_LineCopy, 3294).
--define(wxStyledTextCtrl_MoveCaretInsideView, 3295).
--define(wxStyledTextCtrl_LineLength, 3296).
--define(wxStyledTextCtrl_BraceHighlight, 3297).
--define(wxStyledTextCtrl_BraceBadLight, 3298).
--define(wxStyledTextCtrl_BraceMatch, 3299).
--define(wxStyledTextCtrl_GetViewEOL, 3300).
--define(wxStyledTextCtrl_SetViewEOL, 3301).
--define(wxStyledTextCtrl_SetModEventMask, 3302).
--define(wxStyledTextCtrl_GetEdgeColumn, 3303).
--define(wxStyledTextCtrl_SetEdgeColumn, 3304).
--define(wxStyledTextCtrl_SetEdgeMode, 3305).
--define(wxStyledTextCtrl_GetEdgeMode, 3306).
--define(wxStyledTextCtrl_GetEdgeColour, 3307).
--define(wxStyledTextCtrl_SetEdgeColour, 3308).
--define(wxStyledTextCtrl_SearchAnchor, 3309).
--define(wxStyledTextCtrl_SearchNext, 3310).
--define(wxStyledTextCtrl_SearchPrev, 3311).
--define(wxStyledTextCtrl_LinesOnScreen, 3312).
--define(wxStyledTextCtrl_UsePopUp, 3313).
--define(wxStyledTextCtrl_SelectionIsRectangle, 3314).
--define(wxStyledTextCtrl_SetZoom, 3315).
--define(wxStyledTextCtrl_GetZoom, 3316).
--define(wxStyledTextCtrl_GetModEventMask, 3317).
--define(wxStyledTextCtrl_SetSTCFocus, 3318).
--define(wxStyledTextCtrl_GetSTCFocus, 3319).
--define(wxStyledTextCtrl_SetStatus, 3320).
--define(wxStyledTextCtrl_GetStatus, 3321).
--define(wxStyledTextCtrl_SetMouseDownCaptures, 3322).
--define(wxStyledTextCtrl_GetMouseDownCaptures, 3323).
--define(wxStyledTextCtrl_SetSTCCursor, 3324).
--define(wxStyledTextCtrl_GetSTCCursor, 3325).
--define(wxStyledTextCtrl_SetControlCharSymbol, 3326).
--define(wxStyledTextCtrl_GetControlCharSymbol, 3327).
--define(wxStyledTextCtrl_WordPartLeft, 3328).
--define(wxStyledTextCtrl_WordPartLeftExtend, 3329).
--define(wxStyledTextCtrl_WordPartRight, 3330).
--define(wxStyledTextCtrl_WordPartRightExtend, 3331).
--define(wxStyledTextCtrl_SetVisiblePolicy, 3332).
--define(wxStyledTextCtrl_DelLineLeft, 3333).
--define(wxStyledTextCtrl_DelLineRight, 3334).
--define(wxStyledTextCtrl_GetXOffset, 3335).
--define(wxStyledTextCtrl_ChooseCaretX, 3336).
--define(wxStyledTextCtrl_SetXCaretPolicy, 3337).
--define(wxStyledTextCtrl_SetYCaretPolicy, 3338).
--define(wxStyledTextCtrl_GetPrintWrapMode, 3339).
--define(wxStyledTextCtrl_SetHotspotActiveForeground, 3340).
--define(wxStyledTextCtrl_SetHotspotActiveBackground, 3341).
--define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3342).
--define(wxStyledTextCtrl_SetHotspotSingleLine, 3343).
--define(wxStyledTextCtrl_ParaDownExtend, 3344).
--define(wxStyledTextCtrl_ParaUp, 3345).
--define(wxStyledTextCtrl_ParaUpExtend, 3346).
--define(wxStyledTextCtrl_PositionBefore, 3347).
--define(wxStyledTextCtrl_PositionAfter, 3348).
--define(wxStyledTextCtrl_CopyRange, 3349).
--define(wxStyledTextCtrl_CopyText, 3350).
--define(wxStyledTextCtrl_SetSelectionMode, 3351).
--define(wxStyledTextCtrl_GetSelectionMode, 3352).
--define(wxStyledTextCtrl_LineDownRectExtend, 3353).
--define(wxStyledTextCtrl_LineUpRectExtend, 3354).
--define(wxStyledTextCtrl_CharLeftRectExtend, 3355).
--define(wxStyledTextCtrl_CharRightRectExtend, 3356).
--define(wxStyledTextCtrl_HomeRectExtend, 3357).
--define(wxStyledTextCtrl_VCHomeRectExtend, 3358).
--define(wxStyledTextCtrl_LineEndRectExtend, 3359).
--define(wxStyledTextCtrl_PageUpRectExtend, 3360).
--define(wxStyledTextCtrl_PageDownRectExtend, 3361).
--define(wxStyledTextCtrl_StutteredPageUp, 3362).
--define(wxStyledTextCtrl_StutteredPageUpExtend, 3363).
--define(wxStyledTextCtrl_StutteredPageDown, 3364).
--define(wxStyledTextCtrl_StutteredPageDownExtend, 3365).
--define(wxStyledTextCtrl_WordLeftEnd, 3366).
--define(wxStyledTextCtrl_WordLeftEndExtend, 3367).
--define(wxStyledTextCtrl_WordRightEnd, 3368).
--define(wxStyledTextCtrl_WordRightEndExtend, 3369).
--define(wxStyledTextCtrl_SetWhitespaceChars, 3370).
--define(wxStyledTextCtrl_SetCharsDefault, 3371).
--define(wxStyledTextCtrl_AutoCompGetCurrent, 3372).
--define(wxStyledTextCtrl_Allocate, 3373).
--define(wxStyledTextCtrl_FindColumn, 3374).
--define(wxStyledTextCtrl_GetCaretSticky, 3375).
--define(wxStyledTextCtrl_SetCaretSticky, 3376).
--define(wxStyledTextCtrl_ToggleCaretSticky, 3377).
--define(wxStyledTextCtrl_SetPasteConvertEndings, 3378).
--define(wxStyledTextCtrl_GetPasteConvertEndings, 3379).
--define(wxStyledTextCtrl_SelectionDuplicate, 3380).
--define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3381).
--define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3382).
--define(wxStyledTextCtrl_StartRecord, 3383).
--define(wxStyledTextCtrl_StopRecord, 3384).
--define(wxStyledTextCtrl_SetLexer, 3385).
--define(wxStyledTextCtrl_GetLexer, 3386).
--define(wxStyledTextCtrl_Colourise, 3387).
--define(wxStyledTextCtrl_SetProperty, 3388).
--define(wxStyledTextCtrl_SetKeyWords, 3389).
--define(wxStyledTextCtrl_SetLexerLanguage, 3390).
--define(wxStyledTextCtrl_GetProperty, 3391).
--define(wxStyledTextCtrl_GetStyleBitsNeeded, 3392).
--define(wxStyledTextCtrl_GetCurrentLine, 3393).
--define(wxStyledTextCtrl_StyleSetSpec, 3394).
--define(wxStyledTextCtrl_StyleSetFont, 3395).
--define(wxStyledTextCtrl_StyleSetFontAttr, 3396).
--define(wxStyledTextCtrl_StyleSetCharacterSet, 3397).
--define(wxStyledTextCtrl_StyleSetFontEncoding, 3398).
--define(wxStyledTextCtrl_CmdKeyExecute, 3399).
--define(wxStyledTextCtrl_SetMargins, 3400).
--define(wxStyledTextCtrl_GetSelection, 3401).
--define(wxStyledTextCtrl_PointFromPosition, 3402).
--define(wxStyledTextCtrl_ScrollToLine, 3403).
--define(wxStyledTextCtrl_ScrollToColumn, 3404).
--define(wxStyledTextCtrl_SetVScrollBar, 3405).
--define(wxStyledTextCtrl_SetHScrollBar, 3406).
--define(wxStyledTextCtrl_GetLastKeydownProcessed, 3407).
--define(wxStyledTextCtrl_SetLastKeydownProcessed, 3408).
--define(wxStyledTextCtrl_SaveFile, 3409).
--define(wxStyledTextCtrl_LoadFile, 3410).
--define(wxStyledTextCtrl_DoDragOver, 3411).
--define(wxStyledTextCtrl_DoDropText, 3412).
--define(wxStyledTextCtrl_GetUseAntiAliasing, 3413).
--define(wxStyledTextCtrl_AddTextRaw, 3414).
--define(wxStyledTextCtrl_InsertTextRaw, 3415).
--define(wxStyledTextCtrl_GetCurLineRaw, 3416).
--define(wxStyledTextCtrl_GetLineRaw, 3417).
--define(wxStyledTextCtrl_GetSelectedTextRaw, 3418).
--define(wxStyledTextCtrl_GetTextRangeRaw, 3419).
--define(wxStyledTextCtrl_SetTextRaw, 3420).
--define(wxStyledTextCtrl_GetTextRaw, 3421).
--define(wxStyledTextCtrl_AppendTextRaw, 3422).
--define(wxArtProvider_GetBitmap, 3423).
--define(wxArtProvider_GetIcon, 3424).
--define(wxTreeEvent_GetKeyCode, 3425).
--define(wxTreeEvent_GetItem, 3426).
--define(wxTreeEvent_GetKeyEvent, 3427).
--define(wxTreeEvent_GetLabel, 3428).
--define(wxTreeEvent_GetOldItem, 3429).
--define(wxTreeEvent_GetPoint, 3430).
--define(wxTreeEvent_IsEditCancelled, 3431).
--define(wxTreeEvent_SetToolTip, 3432).
--define(wxNotebookEvent_GetOldSelection, 3433).
--define(wxNotebookEvent_GetSelection, 3434).
--define(wxNotebookEvent_SetOldSelection, 3435).
--define(wxNotebookEvent_SetSelection, 3436).
--define(wxFileDataObject_new, 3437).
--define(wxFileDataObject_AddFile, 3438).
--define(wxFileDataObject_GetFilenames, 3439).
--define(wxFileDataObject_destroy, 3440).
--define(wxTextDataObject_new, 3441).
--define(wxTextDataObject_GetTextLength, 3442).
--define(wxTextDataObject_GetText, 3443).
--define(wxTextDataObject_SetText, 3444).
--define(wxTextDataObject_destroy, 3445).
--define(wxBitmapDataObject_new_1_1, 3446).
--define(wxBitmapDataObject_new_1_0, 3447).
--define(wxBitmapDataObject_GetBitmap, 3448).
--define(wxBitmapDataObject_SetBitmap, 3449).
--define(wxBitmapDataObject_destroy, 3450).
--define(wxClipboard_new, 3452).
--define(wxClipboard_destruct, 3453).
--define(wxClipboard_AddData, 3454).
--define(wxClipboard_Clear, 3455).
--define(wxClipboard_Close, 3456).
--define(wxClipboard_Flush, 3457).
--define(wxClipboard_GetData, 3458).
--define(wxClipboard_IsOpened, 3459).
--define(wxClipboard_Open, 3460).
--define(wxClipboard_SetData, 3461).
--define(wxClipboard_UsePrimarySelection, 3463).
--define(wxClipboard_IsSupported, 3464).
--define(wxClipboard_Get, 3465).
--define(wxSpinEvent_GetPosition, 3466).
--define(wxSpinEvent_SetPosition, 3467).
--define(wxSplitterWindow_new_0, 3468).
--define(wxSplitterWindow_new_2, 3469).
--define(wxSplitterWindow_destruct, 3470).
--define(wxSplitterWindow_Create, 3471).
--define(wxSplitterWindow_GetMinimumPaneSize, 3472).
--define(wxSplitterWindow_GetSashGravity, 3473).
--define(wxSplitterWindow_GetSashPosition, 3474).
--define(wxSplitterWindow_GetSplitMode, 3475).
--define(wxSplitterWindow_GetWindow1, 3476).
--define(wxSplitterWindow_GetWindow2, 3477).
--define(wxSplitterWindow_Initialize, 3478).
--define(wxSplitterWindow_IsSplit, 3479).
--define(wxSplitterWindow_ReplaceWindow, 3480).
--define(wxSplitterWindow_SetSashGravity, 3481).
--define(wxSplitterWindow_SetSashPosition, 3482).
--define(wxSplitterWindow_SetSashSize, 3483).
--define(wxSplitterWindow_SetMinimumPaneSize, 3484).
--define(wxSplitterWindow_SetSplitMode, 3485).
--define(wxSplitterWindow_SplitHorizontally, 3486).
--define(wxSplitterWindow_SplitVertically, 3487).
--define(wxSplitterWindow_Unsplit, 3488).
--define(wxSplitterWindow_UpdateSize, 3489).
--define(wxSplitterEvent_GetSashPosition, 3490).
--define(wxSplitterEvent_GetX, 3491).
--define(wxSplitterEvent_GetY, 3492).
--define(wxSplitterEvent_GetWindowBeingRemoved, 3493).
--define(wxSplitterEvent_SetSashPosition, 3494).
--define(wxHtmlWindow_new_0, 3495).
--define(wxHtmlWindow_new_2, 3496).
--define(wxHtmlWindow_AppendToPage, 3497).
--define(wxHtmlWindow_GetOpenedAnchor, 3498).
--define(wxHtmlWindow_GetOpenedPage, 3499).
--define(wxHtmlWindow_GetOpenedPageTitle, 3500).
--define(wxHtmlWindow_GetRelatedFrame, 3501).
--define(wxHtmlWindow_HistoryBack, 3502).
--define(wxHtmlWindow_HistoryCanBack, 3503).
--define(wxHtmlWindow_HistoryCanForward, 3504).
--define(wxHtmlWindow_HistoryClear, 3505).
--define(wxHtmlWindow_HistoryForward, 3506).
--define(wxHtmlWindow_LoadFile, 3507).
--define(wxHtmlWindow_LoadPage, 3508).
--define(wxHtmlWindow_SelectAll, 3509).
--define(wxHtmlWindow_SelectionToText, 3510).
--define(wxHtmlWindow_SelectLine, 3511).
--define(wxHtmlWindow_SelectWord, 3512).
--define(wxHtmlWindow_SetBorders, 3513).
--define(wxHtmlWindow_SetFonts, 3514).
--define(wxHtmlWindow_SetPage, 3515).
--define(wxHtmlWindow_SetRelatedFrame, 3516).
--define(wxHtmlWindow_SetRelatedStatusBar, 3517).
--define(wxHtmlWindow_ToText, 3518).
--define(wxHtmlWindow_destroy, 3519).
--define(wxHtmlLinkEvent_GetLinkInfo, 3520).
--define(wxSystemSettings_GetColour, 3521).
--define(wxSystemSettings_GetFont, 3522).
--define(wxSystemSettings_GetMetric, 3523).
--define(wxSystemSettings_GetScreenType, 3524).
--define(wxSystemOptions_GetOption, 3525).
--define(wxSystemOptions_GetOptionInt, 3526).
--define(wxSystemOptions_HasOption, 3527).
--define(wxSystemOptions_IsFalse, 3528).
--define(wxSystemOptions_SetOption_2_1, 3529).
--define(wxSystemOptions_SetOption_2_0, 3530).
--define(wxAuiNotebookEvent_SetSelection, 3531).
--define(wxAuiNotebookEvent_GetSelection, 3532).
--define(wxAuiNotebookEvent_SetOldSelection, 3533).
--define(wxAuiNotebookEvent_GetOldSelection, 3534).
--define(wxAuiNotebookEvent_SetDragSource, 3535).
--define(wxAuiNotebookEvent_GetDragSource, 3536).
--define(wxAuiManagerEvent_SetManager, 3537).
--define(wxAuiManagerEvent_GetManager, 3538).
--define(wxAuiManagerEvent_SetPane, 3539).
--define(wxAuiManagerEvent_GetPane, 3540).
--define(wxAuiManagerEvent_SetButton, 3541).
--define(wxAuiManagerEvent_GetButton, 3542).
--define(wxAuiManagerEvent_SetDC, 3543).
--define(wxAuiManagerEvent_GetDC, 3544).
--define(wxAuiManagerEvent_Veto, 3545).
--define(wxAuiManagerEvent_GetVeto, 3546).
--define(wxAuiManagerEvent_SetCanVeto, 3547).
--define(wxAuiManagerEvent_CanVeto, 3548).
--define(wxLogNull_new, 3549).
--define(wxLogNull_destroy, 3550).
--define(wxTaskBarIcon_new, 3551).
--define(wxTaskBarIcon_destruct, 3552).
--define(wxTaskBarIcon_PopupMenu, 3553).
--define(wxTaskBarIcon_RemoveIcon, 3554).
--define(wxTaskBarIcon_SetIcon, 3555).
--define(wxLocale_new_0, 3556).
--define(wxLocale_new_2, 3558).
--define(wxLocale_destruct, 3559).
--define(wxLocale_Init, 3561).
--define(wxLocale_AddCatalog_1, 3562).
--define(wxLocale_AddCatalog_3, 3563).
--define(wxLocale_AddCatalogLookupPathPrefix, 3564).
--define(wxLocale_GetCanonicalName, 3565).
--define(wxLocale_GetLanguage, 3566).
--define(wxLocale_GetLanguageName, 3567).
--define(wxLocale_GetLocale, 3568).
--define(wxLocale_GetName, 3569).
--define(wxLocale_GetString_2, 3570).
--define(wxLocale_GetString_4, 3571).
--define(wxLocale_GetHeaderValue, 3572).
--define(wxLocale_GetSysName, 3573).
--define(wxLocale_GetSystemEncoding, 3574).
--define(wxLocale_GetSystemEncodingName, 3575).
--define(wxLocale_GetSystemLanguage, 3576).
--define(wxLocale_IsLoaded, 3577).
--define(wxLocale_IsOk, 3578).
--define(wxActivateEvent_GetActive, 3579).
--define(wxPopupWindow_new_2, 3581).
--define(wxPopupWindow_new_0, 3582).
--define(wxPopupWindow_destruct, 3584).
--define(wxPopupWindow_Create, 3585).
--define(wxPopupWindow_Position, 3586).
--define(wxPopupTransientWindow_new_0, 3587).
--define(wxPopupTransientWindow_new_2, 3588).
--define(wxPopupTransientWindow_destruct, 3589).
--define(wxPopupTransientWindow_Popup, 3590).
--define(wxPopupTransientWindow_Dismiss, 3591).
--define(wxOverlay_new, 3592).
--define(wxOverlay_destruct, 3593).
--define(wxOverlay_Reset, 3594).
--define(wxDCOverlay_new_6, 3595).
--define(wxDCOverlay_new_2, 3596).
--define(wxDCOverlay_destruct, 3597).
--define(wxDCOverlay_Clear, 3598).
+-define(wxGauge_GetRange, 1605).
+-define(wxGauge_GetValue, 1606).
+-define(wxGauge_IsVertical, 1607).
+-define(wxGauge_SetRange, 1608).
+-define(wxGauge_SetValue, 1609).
+-define(wxGauge_Pulse, 1610).
+-define(wxGauge_destroy, 1611).
+-define(wxGenericDirCtrl_new_0, 1612).
+-define(wxGenericDirCtrl_new_2, 1613).
+-define(wxGenericDirCtrl_destruct, 1614).
+-define(wxGenericDirCtrl_Create, 1615).
+-define(wxGenericDirCtrl_Init, 1616).
+-define(wxGenericDirCtrl_CollapseTree, 1617).
+-define(wxGenericDirCtrl_ExpandPath, 1618).
+-define(wxGenericDirCtrl_GetDefaultPath, 1619).
+-define(wxGenericDirCtrl_GetPath, 1620).
+-define(wxGenericDirCtrl_GetFilePath, 1621).
+-define(wxGenericDirCtrl_GetFilter, 1622).
+-define(wxGenericDirCtrl_GetFilterIndex, 1623).
+-define(wxGenericDirCtrl_GetRootId, 1624).
+-define(wxGenericDirCtrl_GetTreeCtrl, 1625).
+-define(wxGenericDirCtrl_ReCreateTree, 1626).
+-define(wxGenericDirCtrl_SetDefaultPath, 1627).
+-define(wxGenericDirCtrl_SetFilter, 1628).
+-define(wxGenericDirCtrl_SetFilterIndex, 1629).
+-define(wxGenericDirCtrl_SetPath, 1630).
+-define(wxStaticBox_new_4, 1632).
+-define(wxStaticBox_new_0, 1633).
+-define(wxStaticBox_Create, 1634).
+-define(wxStaticBox_destroy, 1635).
+-define(wxStaticLine_new_2, 1637).
+-define(wxStaticLine_new_0, 1638).
+-define(wxStaticLine_Create, 1639).
+-define(wxStaticLine_IsVertical, 1640).
+-define(wxStaticLine_GetDefaultSize, 1641).
+-define(wxStaticLine_destroy, 1642).
+-define(wxListBox_new_3, 1645).
+-define(wxListBox_new_0, 1646).
+-define(wxListBox_destruct, 1648).
+-define(wxListBox_Create, 1650).
+-define(wxListBox_Deselect, 1651).
+-define(wxListBox_GetSelections, 1652).
+-define(wxListBox_InsertItems, 1653).
+-define(wxListBox_IsSelected, 1654).
+-define(wxListBox_Set, 1655).
+-define(wxListBox_HitTest, 1656).
+-define(wxListBox_SetFirstItem_1_0, 1657).
+-define(wxListBox_SetFirstItem_1_1, 1658).
+-define(wxListCtrl_new_0, 1659).
+-define(wxListCtrl_new_2, 1660).
+-define(wxListCtrl_Arrange, 1661).
+-define(wxListCtrl_AssignImageList, 1662).
+-define(wxListCtrl_ClearAll, 1663).
+-define(wxListCtrl_Create, 1664).
+-define(wxListCtrl_DeleteAllItems, 1665).
+-define(wxListCtrl_DeleteColumn, 1666).
+-define(wxListCtrl_DeleteItem, 1667).
+-define(wxListCtrl_EditLabel, 1668).
+-define(wxListCtrl_EnsureVisible, 1669).
+-define(wxListCtrl_FindItem_3_0, 1670).
+-define(wxListCtrl_FindItem_3_1, 1671).
+-define(wxListCtrl_GetColumn, 1672).
+-define(wxListCtrl_GetColumnCount, 1673).
+-define(wxListCtrl_GetColumnWidth, 1674).
+-define(wxListCtrl_GetCountPerPage, 1675).
+-define(wxListCtrl_GetEditControl, 1676).
+-define(wxListCtrl_GetImageList, 1677).
+-define(wxListCtrl_GetItem, 1678).
+-define(wxListCtrl_GetItemBackgroundColour, 1679).
+-define(wxListCtrl_GetItemCount, 1680).
+-define(wxListCtrl_GetItemData, 1681).
+-define(wxListCtrl_GetItemFont, 1682).
+-define(wxListCtrl_GetItemPosition, 1683).
+-define(wxListCtrl_GetItemRect, 1684).
+-define(wxListCtrl_GetItemSpacing, 1685).
+-define(wxListCtrl_GetItemState, 1686).
+-define(wxListCtrl_GetItemText, 1687).
+-define(wxListCtrl_GetItemTextColour, 1688).
+-define(wxListCtrl_GetNextItem, 1689).
+-define(wxListCtrl_GetSelectedItemCount, 1690).
+-define(wxListCtrl_GetTextColour, 1691).
+-define(wxListCtrl_GetTopItem, 1692).
+-define(wxListCtrl_GetViewRect, 1693).
+-define(wxListCtrl_HitTest, 1694).
+-define(wxListCtrl_InsertColumn_2, 1695).
+-define(wxListCtrl_InsertColumn_3, 1696).
+-define(wxListCtrl_InsertItem_1, 1697).
+-define(wxListCtrl_InsertItem_2_1, 1698).
+-define(wxListCtrl_InsertItem_2_0, 1699).
+-define(wxListCtrl_InsertItem_3, 1700).
+-define(wxListCtrl_RefreshItem, 1701).
+-define(wxListCtrl_RefreshItems, 1702).
+-define(wxListCtrl_ScrollList, 1703).
+-define(wxListCtrl_SetBackgroundColour, 1704).
+-define(wxListCtrl_SetColumn, 1705).
+-define(wxListCtrl_SetColumnWidth, 1706).
+-define(wxListCtrl_SetImageList, 1707).
+-define(wxListCtrl_SetItem_1, 1708).
+-define(wxListCtrl_SetItem_4, 1709).
+-define(wxListCtrl_SetItemBackgroundColour, 1710).
+-define(wxListCtrl_SetItemCount, 1711).
+-define(wxListCtrl_SetItemData, 1712).
+-define(wxListCtrl_SetItemFont, 1713).
+-define(wxListCtrl_SetItemImage, 1714).
+-define(wxListCtrl_SetItemColumnImage, 1715).
+-define(wxListCtrl_SetItemPosition, 1716).
+-define(wxListCtrl_SetItemState, 1717).
+-define(wxListCtrl_SetItemText, 1718).
+-define(wxListCtrl_SetItemTextColour, 1719).
+-define(wxListCtrl_SetSingleStyle, 1720).
+-define(wxListCtrl_SetTextColour, 1721).
+-define(wxListCtrl_SetWindowStyleFlag, 1722).
+-define(wxListCtrl_SortItems, 1723).
+-define(wxListCtrl_destroy, 1724).
+-define(wxListView_ClearColumnImage, 1725).
+-define(wxListView_Focus, 1726).
+-define(wxListView_GetFirstSelected, 1727).
+-define(wxListView_GetFocusedItem, 1728).
+-define(wxListView_GetNextSelected, 1729).
+-define(wxListView_IsSelected, 1730).
+-define(wxListView_Select, 1731).
+-define(wxListView_SetColumnImage, 1732).
+-define(wxListItem_new_0, 1733).
+-define(wxListItem_new_1, 1734).
+-define(wxListItem_destruct, 1735).
+-define(wxListItem_Clear, 1736).
+-define(wxListItem_GetAlign, 1737).
+-define(wxListItem_GetBackgroundColour, 1738).
+-define(wxListItem_GetColumn, 1739).
+-define(wxListItem_GetFont, 1740).
+-define(wxListItem_GetId, 1741).
+-define(wxListItem_GetImage, 1742).
+-define(wxListItem_GetMask, 1743).
+-define(wxListItem_GetState, 1744).
+-define(wxListItem_GetText, 1745).
+-define(wxListItem_GetTextColour, 1746).
+-define(wxListItem_GetWidth, 1747).
+-define(wxListItem_SetAlign, 1748).
+-define(wxListItem_SetBackgroundColour, 1749).
+-define(wxListItem_SetColumn, 1750).
+-define(wxListItem_SetFont, 1751).
+-define(wxListItem_SetId, 1752).
+-define(wxListItem_SetImage, 1753).
+-define(wxListItem_SetMask, 1754).
+-define(wxListItem_SetState, 1755).
+-define(wxListItem_SetStateMask, 1756).
+-define(wxListItem_SetText, 1757).
+-define(wxListItem_SetTextColour, 1758).
+-define(wxListItem_SetWidth, 1759).
+-define(wxListItemAttr_new_0, 1760).
+-define(wxListItemAttr_new_3, 1761).
+-define(wxListItemAttr_GetBackgroundColour, 1762).
+-define(wxListItemAttr_GetFont, 1763).
+-define(wxListItemAttr_GetTextColour, 1764).
+-define(wxListItemAttr_HasBackgroundColour, 1765).
+-define(wxListItemAttr_HasFont, 1766).
+-define(wxListItemAttr_HasTextColour, 1767).
+-define(wxListItemAttr_SetBackgroundColour, 1768).
+-define(wxListItemAttr_SetFont, 1769).
+-define(wxListItemAttr_SetTextColour, 1770).
+-define(wxListItemAttr_destroy, 1771).
+-define(wxImageList_new_0, 1772).
+-define(wxImageList_new_3, 1773).
+-define(wxImageList_Add_1, 1774).
+-define(wxImageList_Add_2_0, 1775).
+-define(wxImageList_Add_2_1, 1776).
+-define(wxImageList_Create, 1777).
+-define(wxImageList_Draw, 1779).
+-define(wxImageList_GetBitmap, 1780).
+-define(wxImageList_GetIcon, 1781).
+-define(wxImageList_GetImageCount, 1782).
+-define(wxImageList_GetSize, 1783).
+-define(wxImageList_Remove, 1784).
+-define(wxImageList_RemoveAll, 1785).
+-define(wxImageList_Replace_2, 1786).
+-define(wxImageList_Replace_3, 1787).
+-define(wxImageList_destroy, 1788).
+-define(wxTextAttr_new_0, 1789).
+-define(wxTextAttr_new_2, 1790).
+-define(wxTextAttr_GetAlignment, 1791).
+-define(wxTextAttr_GetBackgroundColour, 1792).
+-define(wxTextAttr_GetFont, 1793).
+-define(wxTextAttr_GetLeftIndent, 1794).
+-define(wxTextAttr_GetLeftSubIndent, 1795).
+-define(wxTextAttr_GetRightIndent, 1796).
+-define(wxTextAttr_GetTabs, 1797).
+-define(wxTextAttr_GetTextColour, 1798).
+-define(wxTextAttr_HasBackgroundColour, 1799).
+-define(wxTextAttr_HasFont, 1800).
+-define(wxTextAttr_HasTextColour, 1801).
+-define(wxTextAttr_GetFlags, 1802).
+-define(wxTextAttr_IsDefault, 1803).
+-define(wxTextAttr_SetAlignment, 1804).
+-define(wxTextAttr_SetBackgroundColour, 1805).
+-define(wxTextAttr_SetFlags, 1806).
+-define(wxTextAttr_SetFont, 1807).
+-define(wxTextAttr_SetLeftIndent, 1808).
+-define(wxTextAttr_SetRightIndent, 1809).
+-define(wxTextAttr_SetTabs, 1810).
+-define(wxTextAttr_SetTextColour, 1811).
+-define(wxTextAttr_destroy, 1812).
+-define(wxTextCtrl_new_3, 1814).
+-define(wxTextCtrl_new_0, 1815).
+-define(wxTextCtrl_destruct, 1817).
+-define(wxTextCtrl_AppendText, 1818).
+-define(wxTextCtrl_CanCopy, 1819).
+-define(wxTextCtrl_CanCut, 1820).
+-define(wxTextCtrl_CanPaste, 1821).
+-define(wxTextCtrl_CanRedo, 1822).
+-define(wxTextCtrl_CanUndo, 1823).
+-define(wxTextCtrl_Clear, 1824).
+-define(wxTextCtrl_Copy, 1825).
+-define(wxTextCtrl_Create, 1826).
+-define(wxTextCtrl_Cut, 1827).
+-define(wxTextCtrl_DiscardEdits, 1828).
+-define(wxTextCtrl_ChangeValue, 1829).
+-define(wxTextCtrl_EmulateKeyPress, 1830).
+-define(wxTextCtrl_GetDefaultStyle, 1831).
+-define(wxTextCtrl_GetInsertionPoint, 1832).
+-define(wxTextCtrl_GetLastPosition, 1833).
+-define(wxTextCtrl_GetLineLength, 1834).
+-define(wxTextCtrl_GetLineText, 1835).
+-define(wxTextCtrl_GetNumberOfLines, 1836).
+-define(wxTextCtrl_GetRange, 1837).
+-define(wxTextCtrl_GetSelection, 1838).
+-define(wxTextCtrl_GetStringSelection, 1839).
+-define(wxTextCtrl_GetStyle, 1840).
+-define(wxTextCtrl_GetValue, 1841).
+-define(wxTextCtrl_IsEditable, 1842).
+-define(wxTextCtrl_IsModified, 1843).
+-define(wxTextCtrl_IsMultiLine, 1844).
+-define(wxTextCtrl_IsSingleLine, 1845).
+-define(wxTextCtrl_LoadFile, 1846).
+-define(wxTextCtrl_MarkDirty, 1847).
+-define(wxTextCtrl_Paste, 1848).
+-define(wxTextCtrl_PositionToXY, 1849).
+-define(wxTextCtrl_Redo, 1850).
+-define(wxTextCtrl_Remove, 1851).
+-define(wxTextCtrl_Replace, 1852).
+-define(wxTextCtrl_SaveFile, 1853).
+-define(wxTextCtrl_SetDefaultStyle, 1854).
+-define(wxTextCtrl_SetEditable, 1855).
+-define(wxTextCtrl_SetInsertionPoint, 1856).
+-define(wxTextCtrl_SetInsertionPointEnd, 1857).
+-define(wxTextCtrl_SetMaxLength, 1859).
+-define(wxTextCtrl_SetSelection, 1860).
+-define(wxTextCtrl_SetStyle, 1861).
+-define(wxTextCtrl_SetValue, 1862).
+-define(wxTextCtrl_ShowPosition, 1863).
+-define(wxTextCtrl_Undo, 1864).
+-define(wxTextCtrl_WriteText, 1865).
+-define(wxTextCtrl_XYToPosition, 1866).
+-define(wxNotebook_new_0, 1869).
+-define(wxNotebook_new_3, 1870).
+-define(wxNotebook_destruct, 1871).
+-define(wxNotebook_AddPage, 1872).
+-define(wxNotebook_AdvanceSelection, 1873).
+-define(wxNotebook_AssignImageList, 1874).
+-define(wxNotebook_Create, 1875).
+-define(wxNotebook_DeleteAllPages, 1876).
+-define(wxNotebook_DeletePage, 1877).
+-define(wxNotebook_RemovePage, 1878).
+-define(wxNotebook_GetCurrentPage, 1879).
+-define(wxNotebook_GetImageList, 1880).
+-define(wxNotebook_GetPage, 1882).
+-define(wxNotebook_GetPageCount, 1883).
+-define(wxNotebook_GetPageImage, 1884).
+-define(wxNotebook_GetPageText, 1885).
+-define(wxNotebook_GetRowCount, 1886).
+-define(wxNotebook_GetSelection, 1887).
+-define(wxNotebook_GetThemeBackgroundColour, 1888).
+-define(wxNotebook_HitTest, 1890).
+-define(wxNotebook_InsertPage, 1892).
+-define(wxNotebook_SetImageList, 1893).
+-define(wxNotebook_SetPadding, 1894).
+-define(wxNotebook_SetPageSize, 1895).
+-define(wxNotebook_SetPageImage, 1896).
+-define(wxNotebook_SetPageText, 1897).
+-define(wxNotebook_SetSelection, 1898).
+-define(wxNotebook_ChangeSelection, 1899).
+-define(wxChoicebook_new_0, 1900).
+-define(wxChoicebook_new_3, 1901).
+-define(wxChoicebook_AddPage, 1902).
+-define(wxChoicebook_AdvanceSelection, 1903).
+-define(wxChoicebook_AssignImageList, 1904).
+-define(wxChoicebook_Create, 1905).
+-define(wxChoicebook_DeleteAllPages, 1906).
+-define(wxChoicebook_DeletePage, 1907).
+-define(wxChoicebook_RemovePage, 1908).
+-define(wxChoicebook_GetCurrentPage, 1909).
+-define(wxChoicebook_GetImageList, 1910).
+-define(wxChoicebook_GetPage, 1912).
+-define(wxChoicebook_GetPageCount, 1913).
+-define(wxChoicebook_GetPageImage, 1914).
+-define(wxChoicebook_GetPageText, 1915).
+-define(wxChoicebook_GetSelection, 1916).
+-define(wxChoicebook_HitTest, 1917).
+-define(wxChoicebook_InsertPage, 1918).
+-define(wxChoicebook_SetImageList, 1919).
+-define(wxChoicebook_SetPageSize, 1920).
+-define(wxChoicebook_SetPageImage, 1921).
+-define(wxChoicebook_SetPageText, 1922).
+-define(wxChoicebook_SetSelection, 1923).
+-define(wxChoicebook_ChangeSelection, 1924).
+-define(wxChoicebook_destroy, 1925).
+-define(wxToolbook_new_0, 1926).
+-define(wxToolbook_new_3, 1927).
+-define(wxToolbook_AddPage, 1928).
+-define(wxToolbook_AdvanceSelection, 1929).
+-define(wxToolbook_AssignImageList, 1930).
+-define(wxToolbook_Create, 1931).
+-define(wxToolbook_DeleteAllPages, 1932).
+-define(wxToolbook_DeletePage, 1933).
+-define(wxToolbook_RemovePage, 1934).
+-define(wxToolbook_GetCurrentPage, 1935).
+-define(wxToolbook_GetImageList, 1936).
+-define(wxToolbook_GetPage, 1938).
+-define(wxToolbook_GetPageCount, 1939).
+-define(wxToolbook_GetPageImage, 1940).
+-define(wxToolbook_GetPageText, 1941).
+-define(wxToolbook_GetSelection, 1942).
+-define(wxToolbook_HitTest, 1944).
+-define(wxToolbook_InsertPage, 1945).
+-define(wxToolbook_SetImageList, 1946).
+-define(wxToolbook_SetPageSize, 1947).
+-define(wxToolbook_SetPageImage, 1948).
+-define(wxToolbook_SetPageText, 1949).
+-define(wxToolbook_SetSelection, 1950).
+-define(wxToolbook_ChangeSelection, 1951).
+-define(wxToolbook_destroy, 1952).
+-define(wxListbook_new_0, 1953).
+-define(wxListbook_new_3, 1954).
+-define(wxListbook_AddPage, 1955).
+-define(wxListbook_AdvanceSelection, 1956).
+-define(wxListbook_AssignImageList, 1957).
+-define(wxListbook_Create, 1958).
+-define(wxListbook_DeleteAllPages, 1959).
+-define(wxListbook_DeletePage, 1960).
+-define(wxListbook_RemovePage, 1961).
+-define(wxListbook_GetCurrentPage, 1962).
+-define(wxListbook_GetImageList, 1963).
+-define(wxListbook_GetPage, 1965).
+-define(wxListbook_GetPageCount, 1966).
+-define(wxListbook_GetPageImage, 1967).
+-define(wxListbook_GetPageText, 1968).
+-define(wxListbook_GetSelection, 1969).
+-define(wxListbook_HitTest, 1971).
+-define(wxListbook_InsertPage, 1972).
+-define(wxListbook_SetImageList, 1973).
+-define(wxListbook_SetPageSize, 1974).
+-define(wxListbook_SetPageImage, 1975).
+-define(wxListbook_SetPageText, 1976).
+-define(wxListbook_SetSelection, 1977).
+-define(wxListbook_ChangeSelection, 1978).
+-define(wxListbook_destroy, 1979).
+-define(wxTreebook_new_0, 1980).
+-define(wxTreebook_new_3, 1981).
+-define(wxTreebook_AddPage, 1982).
+-define(wxTreebook_AdvanceSelection, 1983).
+-define(wxTreebook_AssignImageList, 1984).
+-define(wxTreebook_Create, 1985).
+-define(wxTreebook_DeleteAllPages, 1986).
+-define(wxTreebook_DeletePage, 1987).
+-define(wxTreebook_RemovePage, 1988).
+-define(wxTreebook_GetCurrentPage, 1989).
+-define(wxTreebook_GetImageList, 1990).
+-define(wxTreebook_GetPage, 1992).
+-define(wxTreebook_GetPageCount, 1993).
+-define(wxTreebook_GetPageImage, 1994).
+-define(wxTreebook_GetPageText, 1995).
+-define(wxTreebook_GetSelection, 1996).
+-define(wxTreebook_ExpandNode, 1997).
+-define(wxTreebook_IsNodeExpanded, 1998).
+-define(wxTreebook_HitTest, 2000).
+-define(wxTreebook_InsertPage, 2001).
+-define(wxTreebook_InsertSubPage, 2002).
+-define(wxTreebook_SetImageList, 2003).
+-define(wxTreebook_SetPageSize, 2004).
+-define(wxTreebook_SetPageImage, 2005).
+-define(wxTreebook_SetPageText, 2006).
+-define(wxTreebook_SetSelection, 2007).
+-define(wxTreebook_ChangeSelection, 2008).
+-define(wxTreebook_destroy, 2009).
+-define(wxTreeCtrl_new_2, 2012).
+-define(wxTreeCtrl_new_0, 2013).
+-define(wxTreeCtrl_destruct, 2015).
+-define(wxTreeCtrl_AddRoot, 2016).
+-define(wxTreeCtrl_AppendItem, 2017).
+-define(wxTreeCtrl_AssignImageList, 2018).
+-define(wxTreeCtrl_AssignStateImageList, 2019).
+-define(wxTreeCtrl_Collapse, 2020).
+-define(wxTreeCtrl_CollapseAndReset, 2021).
+-define(wxTreeCtrl_Create, 2022).
+-define(wxTreeCtrl_Delete, 2023).
+-define(wxTreeCtrl_DeleteAllItems, 2024).
+-define(wxTreeCtrl_DeleteChildren, 2025).
+-define(wxTreeCtrl_EditLabel, 2026).
+-define(wxTreeCtrl_EnsureVisible, 2027).
+-define(wxTreeCtrl_Expand, 2028).
+-define(wxTreeCtrl_GetBoundingRect, 2029).
+-define(wxTreeCtrl_GetChildrenCount, 2031).
+-define(wxTreeCtrl_GetCount, 2032).
+-define(wxTreeCtrl_GetEditControl, 2033).
+-define(wxTreeCtrl_GetFirstChild, 2034).
+-define(wxTreeCtrl_GetNextChild, 2035).
+-define(wxTreeCtrl_GetFirstVisibleItem, 2036).
+-define(wxTreeCtrl_GetImageList, 2037).
+-define(wxTreeCtrl_GetIndent, 2038).
+-define(wxTreeCtrl_GetItemBackgroundColour, 2039).
+-define(wxTreeCtrl_GetItemData, 2040).
+-define(wxTreeCtrl_GetItemFont, 2041).
+-define(wxTreeCtrl_GetItemImage_1, 2042).
+-define(wxTreeCtrl_GetItemImage_2, 2043).
+-define(wxTreeCtrl_GetItemText, 2044).
+-define(wxTreeCtrl_GetItemTextColour, 2045).
+-define(wxTreeCtrl_GetLastChild, 2046).
+-define(wxTreeCtrl_GetNextSibling, 2047).
+-define(wxTreeCtrl_GetNextVisible, 2048).
+-define(wxTreeCtrl_GetItemParent, 2049).
+-define(wxTreeCtrl_GetPrevSibling, 2050).
+-define(wxTreeCtrl_GetPrevVisible, 2051).
+-define(wxTreeCtrl_GetRootItem, 2052).
+-define(wxTreeCtrl_GetSelection, 2053).
+-define(wxTreeCtrl_GetSelections, 2054).
+-define(wxTreeCtrl_GetStateImageList, 2055).
+-define(wxTreeCtrl_HitTest, 2056).
+-define(wxTreeCtrl_InsertItem, 2058).
+-define(wxTreeCtrl_IsBold, 2059).
+-define(wxTreeCtrl_IsExpanded, 2060).
+-define(wxTreeCtrl_IsSelected, 2061).
+-define(wxTreeCtrl_IsVisible, 2062).
+-define(wxTreeCtrl_ItemHasChildren, 2063).
+-define(wxTreeCtrl_IsTreeItemIdOk, 2064).
+-define(wxTreeCtrl_PrependItem, 2065).
+-define(wxTreeCtrl_ScrollTo, 2066).
+-define(wxTreeCtrl_SelectItem_1, 2067).
+-define(wxTreeCtrl_SelectItem_2, 2068).
+-define(wxTreeCtrl_SetIndent, 2069).
+-define(wxTreeCtrl_SetImageList, 2070).
+-define(wxTreeCtrl_SetItemBackgroundColour, 2071).
+-define(wxTreeCtrl_SetItemBold, 2072).
+-define(wxTreeCtrl_SetItemData, 2073).
+-define(wxTreeCtrl_SetItemDropHighlight, 2074).
+-define(wxTreeCtrl_SetItemFont, 2075).
+-define(wxTreeCtrl_SetItemHasChildren, 2076).
+-define(wxTreeCtrl_SetItemImage_2, 2077).
+-define(wxTreeCtrl_SetItemImage_3, 2078).
+-define(wxTreeCtrl_SetItemText, 2079).
+-define(wxTreeCtrl_SetItemTextColour, 2080).
+-define(wxTreeCtrl_SetStateImageList, 2081).
+-define(wxTreeCtrl_SetWindowStyle, 2082).
+-define(wxTreeCtrl_SortChildren, 2083).
+-define(wxTreeCtrl_Toggle, 2084).
+-define(wxTreeCtrl_ToggleItemSelection, 2085).
+-define(wxTreeCtrl_Unselect, 2086).
+-define(wxTreeCtrl_UnselectAll, 2087).
+-define(wxTreeCtrl_UnselectItem, 2088).
+-define(wxScrollBar_new_0, 2089).
+-define(wxScrollBar_new_3, 2090).
+-define(wxScrollBar_destruct, 2091).
+-define(wxScrollBar_Create, 2092).
+-define(wxScrollBar_GetRange, 2093).
+-define(wxScrollBar_GetPageSize, 2094).
+-define(wxScrollBar_GetThumbPosition, 2095).
+-define(wxScrollBar_GetThumbSize, 2096).
+-define(wxScrollBar_SetThumbPosition, 2097).
+-define(wxScrollBar_SetScrollbar, 2098).
+-define(wxSpinButton_new_2, 2100).
+-define(wxSpinButton_new_0, 2101).
+-define(wxSpinButton_Create, 2102).
+-define(wxSpinButton_GetMax, 2103).
+-define(wxSpinButton_GetMin, 2104).
+-define(wxSpinButton_GetValue, 2105).
+-define(wxSpinButton_SetRange, 2106).
+-define(wxSpinButton_SetValue, 2107).
+-define(wxSpinButton_destroy, 2108).
+-define(wxSpinCtrl_new_0, 2109).
+-define(wxSpinCtrl_new_2, 2110).
+-define(wxSpinCtrl_Create, 2112).
+-define(wxSpinCtrl_SetValue_1_1, 2115).
+-define(wxSpinCtrl_SetValue_1_0, 2116).
+-define(wxSpinCtrl_GetValue, 2118).
+-define(wxSpinCtrl_SetRange, 2120).
+-define(wxSpinCtrl_SetSelection, 2121).
+-define(wxSpinCtrl_GetMin, 2123).
+-define(wxSpinCtrl_GetMax, 2125).
+-define(wxSpinCtrl_destroy, 2126).
+-define(wxStaticText_new_0, 2127).
+-define(wxStaticText_new_4, 2128).
+-define(wxStaticText_Create, 2129).
+-define(wxStaticText_GetLabel, 2130).
+-define(wxStaticText_SetLabel, 2131).
+-define(wxStaticText_Wrap, 2132).
+-define(wxStaticText_destroy, 2133).
+-define(wxStaticBitmap_new_0, 2134).
+-define(wxStaticBitmap_new_4, 2135).
+-define(wxStaticBitmap_Create, 2136).
+-define(wxStaticBitmap_GetBitmap, 2137).
+-define(wxStaticBitmap_SetBitmap, 2138).
+-define(wxStaticBitmap_destroy, 2139).
+-define(wxRadioBox_new, 2140).
+-define(wxRadioBox_destruct, 2142).
+-define(wxRadioBox_Create, 2143).
+-define(wxRadioBox_Enable_2, 2144).
+-define(wxRadioBox_Enable_1, 2145).
+-define(wxRadioBox_GetSelection, 2146).
+-define(wxRadioBox_GetString, 2147).
+-define(wxRadioBox_SetSelection, 2148).
+-define(wxRadioBox_Show_2, 2149).
+-define(wxRadioBox_Show_1, 2150).
+-define(wxRadioBox_GetColumnCount, 2151).
+-define(wxRadioBox_GetItemHelpText, 2152).
+-define(wxRadioBox_GetItemToolTip, 2153).
+-define(wxRadioBox_GetItemFromPoint, 2155).
+-define(wxRadioBox_GetRowCount, 2156).
+-define(wxRadioBox_IsItemEnabled, 2157).
+-define(wxRadioBox_IsItemShown, 2158).
+-define(wxRadioBox_SetItemHelpText, 2159).
+-define(wxRadioBox_SetItemToolTip, 2160).
+-define(wxRadioButton_new_0, 2161).
+-define(wxRadioButton_new_4, 2162).
+-define(wxRadioButton_Create, 2163).
+-define(wxRadioButton_GetValue, 2164).
+-define(wxRadioButton_SetValue, 2165).
+-define(wxRadioButton_destroy, 2166).
+-define(wxSlider_new_6, 2168).
+-define(wxSlider_new_0, 2169).
+-define(wxSlider_Create, 2170).
+-define(wxSlider_GetLineSize, 2171).
+-define(wxSlider_GetMax, 2172).
+-define(wxSlider_GetMin, 2173).
+-define(wxSlider_GetPageSize, 2174).
+-define(wxSlider_GetThumbLength, 2175).
+-define(wxSlider_GetValue, 2176).
+-define(wxSlider_SetLineSize, 2177).
+-define(wxSlider_SetPageSize, 2178).
+-define(wxSlider_SetRange, 2179).
+-define(wxSlider_SetThumbLength, 2180).
+-define(wxSlider_SetValue, 2181).
+-define(wxSlider_destroy, 2182).
+-define(wxDialog_new_4, 2184).
+-define(wxDialog_new_0, 2185).
+-define(wxDialog_destruct, 2187).
+-define(wxDialog_Create, 2188).
+-define(wxDialog_CreateButtonSizer, 2189).
+-define(wxDialog_CreateStdDialogButtonSizer, 2190).
+-define(wxDialog_EndModal, 2191).
+-define(wxDialog_GetAffirmativeId, 2192).
+-define(wxDialog_GetReturnCode, 2193).
+-define(wxDialog_IsModal, 2194).
+-define(wxDialog_SetAffirmativeId, 2195).
+-define(wxDialog_SetReturnCode, 2196).
+-define(wxDialog_Show, 2197).
+-define(wxDialog_ShowModal, 2198).
+-define(wxColourDialog_new_0, 2199).
+-define(wxColourDialog_new_2, 2200).
+-define(wxColourDialog_destruct, 2201).
+-define(wxColourDialog_Create, 2202).
+-define(wxColourDialog_GetColourData, 2203).
+-define(wxColourData_new_0, 2204).
+-define(wxColourData_new_1, 2205).
+-define(wxColourData_destruct, 2206).
+-define(wxColourData_GetChooseFull, 2207).
+-define(wxColourData_GetColour, 2208).
+-define(wxColourData_GetCustomColour, 2210).
+-define(wxColourData_SetChooseFull, 2211).
+-define(wxColourData_SetColour, 2212).
+-define(wxColourData_SetCustomColour, 2213).
+-define(wxPalette_new_0, 2214).
+-define(wxPalette_new_4, 2215).
+-define(wxPalette_destruct, 2217).
+-define(wxPalette_Create, 2218).
+-define(wxPalette_GetColoursCount, 2219).
+-define(wxPalette_GetPixel, 2220).
+-define(wxPalette_GetRGB, 2221).
+-define(wxPalette_IsOk, 2222).
+-define(wxDirDialog_new, 2226).
+-define(wxDirDialog_destruct, 2227).
+-define(wxDirDialog_GetPath, 2228).
+-define(wxDirDialog_GetMessage, 2229).
+-define(wxDirDialog_SetMessage, 2230).
+-define(wxDirDialog_SetPath, 2231).
+-define(wxFileDialog_new, 2235).
+-define(wxFileDialog_destruct, 2236).
+-define(wxFileDialog_GetDirectory, 2237).
+-define(wxFileDialog_GetFilename, 2238).
+-define(wxFileDialog_GetFilenames, 2239).
+-define(wxFileDialog_GetFilterIndex, 2240).
+-define(wxFileDialog_GetMessage, 2241).
+-define(wxFileDialog_GetPath, 2242).
+-define(wxFileDialog_GetPaths, 2243).
+-define(wxFileDialog_GetWildcard, 2244).
+-define(wxFileDialog_SetDirectory, 2245).
+-define(wxFileDialog_SetFilename, 2246).
+-define(wxFileDialog_SetFilterIndex, 2247).
+-define(wxFileDialog_SetMessage, 2248).
+-define(wxFileDialog_SetPath, 2249).
+-define(wxFileDialog_SetWildcard, 2250).
+-define(wxPickerBase_SetInternalMargin, 2251).
+-define(wxPickerBase_GetInternalMargin, 2252).
+-define(wxPickerBase_SetTextCtrlProportion, 2253).
+-define(wxPickerBase_SetPickerCtrlProportion, 2254).
+-define(wxPickerBase_GetTextCtrlProportion, 2255).
+-define(wxPickerBase_GetPickerCtrlProportion, 2256).
+-define(wxPickerBase_HasTextCtrl, 2257).
+-define(wxPickerBase_GetTextCtrl, 2258).
+-define(wxPickerBase_IsTextCtrlGrowable, 2259).
+-define(wxPickerBase_SetPickerCtrlGrowable, 2260).
+-define(wxPickerBase_SetTextCtrlGrowable, 2261).
+-define(wxPickerBase_IsPickerCtrlGrowable, 2262).
+-define(wxFilePickerCtrl_new_0, 2263).
+-define(wxFilePickerCtrl_new_3, 2264).
+-define(wxFilePickerCtrl_Create, 2265).
+-define(wxFilePickerCtrl_GetPath, 2266).
+-define(wxFilePickerCtrl_SetPath, 2267).
+-define(wxFilePickerCtrl_destroy, 2268).
+-define(wxDirPickerCtrl_new_0, 2269).
+-define(wxDirPickerCtrl_new_3, 2270).
+-define(wxDirPickerCtrl_Create, 2271).
+-define(wxDirPickerCtrl_GetPath, 2272).
+-define(wxDirPickerCtrl_SetPath, 2273).
+-define(wxDirPickerCtrl_destroy, 2274).
+-define(wxColourPickerCtrl_new_0, 2275).
+-define(wxColourPickerCtrl_new_3, 2276).
+-define(wxColourPickerCtrl_Create, 2277).
+-define(wxColourPickerCtrl_GetColour, 2278).
+-define(wxColourPickerCtrl_SetColour_1_1, 2279).
+-define(wxColourPickerCtrl_SetColour_1_0, 2280).
+-define(wxColourPickerCtrl_destroy, 2281).
+-define(wxDatePickerCtrl_new_0, 2282).
+-define(wxDatePickerCtrl_new_3, 2283).
+-define(wxDatePickerCtrl_GetRange, 2284).
+-define(wxDatePickerCtrl_GetValue, 2285).
+-define(wxDatePickerCtrl_SetRange, 2286).
+-define(wxDatePickerCtrl_SetValue, 2287).
+-define(wxDatePickerCtrl_destroy, 2288).
+-define(wxFontPickerCtrl_new_0, 2289).
+-define(wxFontPickerCtrl_new_3, 2290).
+-define(wxFontPickerCtrl_Create, 2291).
+-define(wxFontPickerCtrl_GetSelectedFont, 2292).
+-define(wxFontPickerCtrl_SetSelectedFont, 2293).
+-define(wxFontPickerCtrl_GetMaxPointSize, 2294).
+-define(wxFontPickerCtrl_SetMaxPointSize, 2295).
+-define(wxFontPickerCtrl_destroy, 2296).
+-define(wxFindReplaceDialog_new_0, 2299).
+-define(wxFindReplaceDialog_new_4, 2300).
+-define(wxFindReplaceDialog_destruct, 2301).
+-define(wxFindReplaceDialog_Create, 2302).
+-define(wxFindReplaceDialog_GetData, 2303).
+-define(wxFindReplaceData_new_0, 2304).
+-define(wxFindReplaceData_new_1, 2305).
+-define(wxFindReplaceData_GetFindString, 2306).
+-define(wxFindReplaceData_GetReplaceString, 2307).
+-define(wxFindReplaceData_GetFlags, 2308).
+-define(wxFindReplaceData_SetFlags, 2309).
+-define(wxFindReplaceData_SetFindString, 2310).
+-define(wxFindReplaceData_SetReplaceString, 2311).
+-define(wxFindReplaceData_destroy, 2312).
+-define(wxMultiChoiceDialog_new_0, 2313).
+-define(wxMultiChoiceDialog_new_5, 2315).
+-define(wxMultiChoiceDialog_GetSelections, 2316).
+-define(wxMultiChoiceDialog_SetSelections, 2317).
+-define(wxMultiChoiceDialog_destroy, 2318).
+-define(wxSingleChoiceDialog_new_0, 2319).
+-define(wxSingleChoiceDialog_new_5, 2321).
+-define(wxSingleChoiceDialog_GetSelection, 2322).
+-define(wxSingleChoiceDialog_GetStringSelection, 2323).
+-define(wxSingleChoiceDialog_SetSelection, 2324).
+-define(wxSingleChoiceDialog_destroy, 2325).
+-define(wxTextEntryDialog_new, 2326).
+-define(wxTextEntryDialog_GetValue, 2327).
+-define(wxTextEntryDialog_SetValue, 2328).
+-define(wxTextEntryDialog_destroy, 2329).
+-define(wxPasswordEntryDialog_new, 2330).
+-define(wxPasswordEntryDialog_destroy, 2331).
+-define(wxFontData_new_0, 2332).
+-define(wxFontData_new_1, 2333).
+-define(wxFontData_destruct, 2334).
+-define(wxFontData_EnableEffects, 2335).
+-define(wxFontData_GetAllowSymbols, 2336).
+-define(wxFontData_GetColour, 2337).
+-define(wxFontData_GetChosenFont, 2338).
+-define(wxFontData_GetEnableEffects, 2339).
+-define(wxFontData_GetInitialFont, 2340).
+-define(wxFontData_GetShowHelp, 2341).
+-define(wxFontData_SetAllowSymbols, 2342).
+-define(wxFontData_SetChosenFont, 2343).
+-define(wxFontData_SetColour, 2344).
+-define(wxFontData_SetInitialFont, 2345).
+-define(wxFontData_SetRange, 2346).
+-define(wxFontData_SetShowHelp, 2347).
+-define(wxFontDialog_new_0, 2351).
+-define(wxFontDialog_new_2, 2353).
+-define(wxFontDialog_Create, 2355).
+-define(wxFontDialog_GetFontData, 2356).
+-define(wxFontDialog_destroy, 2358).
+-define(wxProgressDialog_new, 2359).
+-define(wxProgressDialog_destruct, 2360).
+-define(wxProgressDialog_Resume, 2361).
+-define(wxProgressDialog_Update_2, 2362).
+-define(wxProgressDialog_Update_0, 2363).
+-define(wxMessageDialog_new, 2364).
+-define(wxMessageDialog_destruct, 2365).
+-define(wxPageSetupDialog_new, 2366).
+-define(wxPageSetupDialog_destruct, 2367).
+-define(wxPageSetupDialog_GetPageSetupData, 2368).
+-define(wxPageSetupDialog_ShowModal, 2369).
+-define(wxPageSetupDialogData_new_0, 2370).
+-define(wxPageSetupDialogData_new_1_0, 2371).
+-define(wxPageSetupDialogData_new_1_1, 2372).
+-define(wxPageSetupDialogData_destruct, 2373).
+-define(wxPageSetupDialogData_EnableHelp, 2374).
+-define(wxPageSetupDialogData_EnableMargins, 2375).
+-define(wxPageSetupDialogData_EnableOrientation, 2376).
+-define(wxPageSetupDialogData_EnablePaper, 2377).
+-define(wxPageSetupDialogData_EnablePrinter, 2378).
+-define(wxPageSetupDialogData_GetDefaultMinMargins, 2379).
+-define(wxPageSetupDialogData_GetEnableMargins, 2380).
+-define(wxPageSetupDialogData_GetEnableOrientation, 2381).
+-define(wxPageSetupDialogData_GetEnablePaper, 2382).
+-define(wxPageSetupDialogData_GetEnablePrinter, 2383).
+-define(wxPageSetupDialogData_GetEnableHelp, 2384).
+-define(wxPageSetupDialogData_GetDefaultInfo, 2385).
+-define(wxPageSetupDialogData_GetMarginTopLeft, 2386).
+-define(wxPageSetupDialogData_GetMarginBottomRight, 2387).
+-define(wxPageSetupDialogData_GetMinMarginTopLeft, 2388).
+-define(wxPageSetupDialogData_GetMinMarginBottomRight, 2389).
+-define(wxPageSetupDialogData_GetPaperId, 2390).
+-define(wxPageSetupDialogData_GetPaperSize, 2391).
+-define(wxPageSetupDialogData_GetPrintData, 2393).
+-define(wxPageSetupDialogData_IsOk, 2394).
+-define(wxPageSetupDialogData_SetDefaultInfo, 2395).
+-define(wxPageSetupDialogData_SetDefaultMinMargins, 2396).
+-define(wxPageSetupDialogData_SetMarginTopLeft, 2397).
+-define(wxPageSetupDialogData_SetMarginBottomRight, 2398).
+-define(wxPageSetupDialogData_SetMinMarginTopLeft, 2399).
+-define(wxPageSetupDialogData_SetMinMarginBottomRight, 2400).
+-define(wxPageSetupDialogData_SetPaperId, 2401).
+-define(wxPageSetupDialogData_SetPaperSize_1_1, 2402).
+-define(wxPageSetupDialogData_SetPaperSize_1_0, 2403).
+-define(wxPageSetupDialogData_SetPrintData, 2404).
+-define(wxPrintDialog_new_2_0, 2405).
+-define(wxPrintDialog_new_2_1, 2406).
+-define(wxPrintDialog_destruct, 2407).
+-define(wxPrintDialog_GetPrintDialogData, 2408).
+-define(wxPrintDialog_GetPrintDC, 2409).
+-define(wxPrintDialogData_new_0, 2410).
+-define(wxPrintDialogData_new_1_1, 2411).
+-define(wxPrintDialogData_new_1_0, 2412).
+-define(wxPrintDialogData_destruct, 2413).
+-define(wxPrintDialogData_EnableHelp, 2414).
+-define(wxPrintDialogData_EnablePageNumbers, 2415).
+-define(wxPrintDialogData_EnablePrintToFile, 2416).
+-define(wxPrintDialogData_EnableSelection, 2417).
+-define(wxPrintDialogData_GetAllPages, 2418).
+-define(wxPrintDialogData_GetCollate, 2419).
+-define(wxPrintDialogData_GetFromPage, 2420).
+-define(wxPrintDialogData_GetMaxPage, 2421).
+-define(wxPrintDialogData_GetMinPage, 2422).
+-define(wxPrintDialogData_GetNoCopies, 2423).
+-define(wxPrintDialogData_GetPrintData, 2424).
+-define(wxPrintDialogData_GetPrintToFile, 2425).
+-define(wxPrintDialogData_GetSelection, 2426).
+-define(wxPrintDialogData_GetToPage, 2427).
+-define(wxPrintDialogData_IsOk, 2428).
+-define(wxPrintDialogData_SetCollate, 2429).
+-define(wxPrintDialogData_SetFromPage, 2430).
+-define(wxPrintDialogData_SetMaxPage, 2431).
+-define(wxPrintDialogData_SetMinPage, 2432).
+-define(wxPrintDialogData_SetNoCopies, 2433).
+-define(wxPrintDialogData_SetPrintData, 2434).
+-define(wxPrintDialogData_SetPrintToFile, 2435).
+-define(wxPrintDialogData_SetSelection, 2436).
+-define(wxPrintDialogData_SetToPage, 2437).
+-define(wxPrintData_new_0, 2438).
+-define(wxPrintData_new_1, 2439).
+-define(wxPrintData_destruct, 2440).
+-define(wxPrintData_GetCollate, 2441).
+-define(wxPrintData_GetBin, 2442).
+-define(wxPrintData_GetColour, 2443).
+-define(wxPrintData_GetDuplex, 2444).
+-define(wxPrintData_GetNoCopies, 2445).
+-define(wxPrintData_GetOrientation, 2446).
+-define(wxPrintData_GetPaperId, 2447).
+-define(wxPrintData_GetPrinterName, 2448).
+-define(wxPrintData_GetQuality, 2449).
+-define(wxPrintData_IsOk, 2450).
+-define(wxPrintData_SetBin, 2451).
+-define(wxPrintData_SetCollate, 2452).
+-define(wxPrintData_SetColour, 2453).
+-define(wxPrintData_SetDuplex, 2454).
+-define(wxPrintData_SetNoCopies, 2455).
+-define(wxPrintData_SetOrientation, 2456).
+-define(wxPrintData_SetPaperId, 2457).
+-define(wxPrintData_SetPrinterName, 2458).
+-define(wxPrintData_SetQuality, 2459).
+-define(wxPrintPreview_new_2, 2462).
+-define(wxPrintPreview_new_3, 2463).
+-define(wxPrintPreview_destruct, 2465).
+-define(wxPrintPreview_GetCanvas, 2466).
+-define(wxPrintPreview_GetCurrentPage, 2467).
+-define(wxPrintPreview_GetFrame, 2468).
+-define(wxPrintPreview_GetMaxPage, 2469).
+-define(wxPrintPreview_GetMinPage, 2470).
+-define(wxPrintPreview_GetPrintout, 2471).
+-define(wxPrintPreview_GetPrintoutForPrinting, 2472).
+-define(wxPrintPreview_IsOk, 2473).
+-define(wxPrintPreview_PaintPage, 2474).
+-define(wxPrintPreview_Print, 2475).
+-define(wxPrintPreview_RenderPage, 2476).
+-define(wxPrintPreview_SetCanvas, 2477).
+-define(wxPrintPreview_SetCurrentPage, 2478).
+-define(wxPrintPreview_SetFrame, 2479).
+-define(wxPrintPreview_SetPrintout, 2480).
+-define(wxPrintPreview_SetZoom, 2481).
+-define(wxPreviewFrame_new, 2482).
+-define(wxPreviewFrame_destruct, 2483).
+-define(wxPreviewFrame_CreateControlBar, 2484).
+-define(wxPreviewFrame_CreateCanvas, 2485).
+-define(wxPreviewFrame_Initialize, 2486).
+-define(wxPreviewFrame_OnCloseWindow, 2487).
+-define(wxPreviewControlBar_new, 2488).
+-define(wxPreviewControlBar_destruct, 2489).
+-define(wxPreviewControlBar_CreateButtons, 2490).
+-define(wxPreviewControlBar_GetPrintPreview, 2491).
+-define(wxPreviewControlBar_GetZoomControl, 2492).
+-define(wxPreviewControlBar_SetZoomControl, 2493).
+-define(wxPrinter_new, 2495).
+-define(wxPrinter_CreateAbortWindow, 2496).
+-define(wxPrinter_GetAbort, 2497).
+-define(wxPrinter_GetLastError, 2498).
+-define(wxPrinter_GetPrintDialogData, 2499).
+-define(wxPrinter_Print, 2500).
+-define(wxPrinter_PrintDialog, 2501).
+-define(wxPrinter_ReportError, 2502).
+-define(wxPrinter_Setup, 2503).
+-define(wxPrinter_destroy, 2504).
+-define(wxXmlResource_new_1, 2505).
+-define(wxXmlResource_new_2, 2506).
+-define(wxXmlResource_destruct, 2507).
+-define(wxXmlResource_AttachUnknownControl, 2508).
+-define(wxXmlResource_ClearHandlers, 2509).
+-define(wxXmlResource_CompareVersion, 2510).
+-define(wxXmlResource_Get, 2511).
+-define(wxXmlResource_GetFlags, 2512).
+-define(wxXmlResource_GetVersion, 2513).
+-define(wxXmlResource_GetXRCID, 2514).
+-define(wxXmlResource_InitAllHandlers, 2515).
+-define(wxXmlResource_Load, 2516).
+-define(wxXmlResource_LoadBitmap, 2517).
+-define(wxXmlResource_LoadDialog_2, 2518).
+-define(wxXmlResource_LoadDialog_3, 2519).
+-define(wxXmlResource_LoadFrame_2, 2520).
+-define(wxXmlResource_LoadFrame_3, 2521).
+-define(wxXmlResource_LoadIcon, 2522).
+-define(wxXmlResource_LoadMenu, 2523).
+-define(wxXmlResource_LoadMenuBar_2, 2524).
+-define(wxXmlResource_LoadMenuBar_1, 2525).
+-define(wxXmlResource_LoadPanel_2, 2526).
+-define(wxXmlResource_LoadPanel_3, 2527).
+-define(wxXmlResource_LoadToolBar, 2528).
+-define(wxXmlResource_Set, 2529).
+-define(wxXmlResource_SetFlags, 2530).
+-define(wxXmlResource_Unload, 2531).
+-define(wxXmlResource_xrcctrl, 2532).
+-define(wxHtmlEasyPrinting_new, 2533).
+-define(wxHtmlEasyPrinting_destruct, 2534).
+-define(wxHtmlEasyPrinting_GetPrintData, 2535).
+-define(wxHtmlEasyPrinting_GetPageSetupData, 2536).
+-define(wxHtmlEasyPrinting_PreviewFile, 2537).
+-define(wxHtmlEasyPrinting_PreviewText, 2538).
+-define(wxHtmlEasyPrinting_PrintFile, 2539).
+-define(wxHtmlEasyPrinting_PrintText, 2540).
+-define(wxHtmlEasyPrinting_PageSetup, 2541).
+-define(wxHtmlEasyPrinting_SetFonts, 2542).
+-define(wxHtmlEasyPrinting_SetHeader, 2543).
+-define(wxHtmlEasyPrinting_SetFooter, 2544).
+-define(wxGLCanvas_new_2, 2546).
+-define(wxGLCanvas_new_3_1, 2547).
+-define(wxGLCanvas_new_3_0, 2548).
+-define(wxGLCanvas_GetContext, 2549).
+-define(wxGLCanvas_SetCurrent, 2551).
+-define(wxGLCanvas_SwapBuffers, 2552).
+-define(wxGLCanvas_destroy, 2553).
+-define(wxAuiManager_new, 2554).
+-define(wxAuiManager_destruct, 2555).
+-define(wxAuiManager_AddPane_2_1, 2556).
+-define(wxAuiManager_AddPane_3, 2557).
+-define(wxAuiManager_AddPane_2_0, 2558).
+-define(wxAuiManager_DetachPane, 2559).
+-define(wxAuiManager_GetAllPanes, 2560).
+-define(wxAuiManager_GetArtProvider, 2561).
+-define(wxAuiManager_GetDockSizeConstraint, 2562).
+-define(wxAuiManager_GetFlags, 2563).
+-define(wxAuiManager_GetManagedWindow, 2564).
+-define(wxAuiManager_GetManager, 2565).
+-define(wxAuiManager_GetPane_1_1, 2566).
+-define(wxAuiManager_GetPane_1_0, 2567).
+-define(wxAuiManager_HideHint, 2568).
+-define(wxAuiManager_InsertPane, 2569).
+-define(wxAuiManager_LoadPaneInfo, 2570).
+-define(wxAuiManager_LoadPerspective, 2571).
+-define(wxAuiManager_SavePaneInfo, 2572).
+-define(wxAuiManager_SavePerspective, 2573).
+-define(wxAuiManager_SetArtProvider, 2574).
+-define(wxAuiManager_SetDockSizeConstraint, 2575).
+-define(wxAuiManager_SetFlags, 2576).
+-define(wxAuiManager_SetManagedWindow, 2577).
+-define(wxAuiManager_ShowHint, 2578).
+-define(wxAuiManager_UnInit, 2579).
+-define(wxAuiManager_Update, 2580).
+-define(wxAuiPaneInfo_new_0, 2581).
+-define(wxAuiPaneInfo_new_1, 2582).
+-define(wxAuiPaneInfo_destruct, 2583).
+-define(wxAuiPaneInfo_BestSize_1, 2584).
+-define(wxAuiPaneInfo_BestSize_2, 2585).
+-define(wxAuiPaneInfo_Bottom, 2586).
+-define(wxAuiPaneInfo_BottomDockable, 2587).
+-define(wxAuiPaneInfo_Caption, 2588).
+-define(wxAuiPaneInfo_CaptionVisible, 2589).
+-define(wxAuiPaneInfo_Centre, 2590).
+-define(wxAuiPaneInfo_CentrePane, 2591).
+-define(wxAuiPaneInfo_CloseButton, 2592).
+-define(wxAuiPaneInfo_DefaultPane, 2593).
+-define(wxAuiPaneInfo_DestroyOnClose, 2594).
+-define(wxAuiPaneInfo_Direction, 2595).
+-define(wxAuiPaneInfo_Dock, 2596).
+-define(wxAuiPaneInfo_Dockable, 2597).
+-define(wxAuiPaneInfo_Fixed, 2598).
+-define(wxAuiPaneInfo_Float, 2599).
+-define(wxAuiPaneInfo_Floatable, 2600).
+-define(wxAuiPaneInfo_FloatingPosition_1, 2601).
+-define(wxAuiPaneInfo_FloatingPosition_2, 2602).
+-define(wxAuiPaneInfo_FloatingSize_1, 2603).
+-define(wxAuiPaneInfo_FloatingSize_2, 2604).
+-define(wxAuiPaneInfo_Gripper, 2605).
+-define(wxAuiPaneInfo_GripperTop, 2606).
+-define(wxAuiPaneInfo_HasBorder, 2607).
+-define(wxAuiPaneInfo_HasCaption, 2608).
+-define(wxAuiPaneInfo_HasCloseButton, 2609).
+-define(wxAuiPaneInfo_HasFlag, 2610).
+-define(wxAuiPaneInfo_HasGripper, 2611).
+-define(wxAuiPaneInfo_HasGripperTop, 2612).
+-define(wxAuiPaneInfo_HasMaximizeButton, 2613).
+-define(wxAuiPaneInfo_HasMinimizeButton, 2614).
+-define(wxAuiPaneInfo_HasPinButton, 2615).
+-define(wxAuiPaneInfo_Hide, 2616).
+-define(wxAuiPaneInfo_IsBottomDockable, 2617).
+-define(wxAuiPaneInfo_IsDocked, 2618).
+-define(wxAuiPaneInfo_IsFixed, 2619).
+-define(wxAuiPaneInfo_IsFloatable, 2620).
+-define(wxAuiPaneInfo_IsFloating, 2621).
+-define(wxAuiPaneInfo_IsLeftDockable, 2622).
+-define(wxAuiPaneInfo_IsMovable, 2623).
+-define(wxAuiPaneInfo_IsOk, 2624).
+-define(wxAuiPaneInfo_IsResizable, 2625).
+-define(wxAuiPaneInfo_IsRightDockable, 2626).
+-define(wxAuiPaneInfo_IsShown, 2627).
+-define(wxAuiPaneInfo_IsToolbar, 2628).
+-define(wxAuiPaneInfo_IsTopDockable, 2629).
+-define(wxAuiPaneInfo_Layer, 2630).
+-define(wxAuiPaneInfo_Left, 2631).
+-define(wxAuiPaneInfo_LeftDockable, 2632).
+-define(wxAuiPaneInfo_MaxSize_1, 2633).
+-define(wxAuiPaneInfo_MaxSize_2, 2634).
+-define(wxAuiPaneInfo_MaximizeButton, 2635).
+-define(wxAuiPaneInfo_MinSize_1, 2636).
+-define(wxAuiPaneInfo_MinSize_2, 2637).
+-define(wxAuiPaneInfo_MinimizeButton, 2638).
+-define(wxAuiPaneInfo_Movable, 2639).
+-define(wxAuiPaneInfo_Name, 2640).
+-define(wxAuiPaneInfo_PaneBorder, 2641).
+-define(wxAuiPaneInfo_PinButton, 2642).
+-define(wxAuiPaneInfo_Position, 2643).
+-define(wxAuiPaneInfo_Resizable, 2644).
+-define(wxAuiPaneInfo_Right, 2645).
+-define(wxAuiPaneInfo_RightDockable, 2646).
+-define(wxAuiPaneInfo_Row, 2647).
+-define(wxAuiPaneInfo_SafeSet, 2648).
+-define(wxAuiPaneInfo_SetFlag, 2649).
+-define(wxAuiPaneInfo_Show, 2650).
+-define(wxAuiPaneInfo_ToolbarPane, 2651).
+-define(wxAuiPaneInfo_Top, 2652).
+-define(wxAuiPaneInfo_TopDockable, 2653).
+-define(wxAuiPaneInfo_Window, 2654).
+-define(wxAuiPaneInfo_GetWindow, 2655).
+-define(wxAuiPaneInfo_GetFrame, 2656).
+-define(wxAuiPaneInfo_GetDirection, 2657).
+-define(wxAuiPaneInfo_GetLayer, 2658).
+-define(wxAuiPaneInfo_GetRow, 2659).
+-define(wxAuiPaneInfo_GetPosition, 2660).
+-define(wxAuiPaneInfo_GetFloatingPosition, 2661).
+-define(wxAuiPaneInfo_GetFloatingSize, 2662).
+-define(wxAuiNotebook_new_0, 2663).
+-define(wxAuiNotebook_new_2, 2664).
+-define(wxAuiNotebook_AddPage, 2665).
+-define(wxAuiNotebook_Create, 2666).
+-define(wxAuiNotebook_DeletePage, 2667).
+-define(wxAuiNotebook_GetArtProvider, 2668).
+-define(wxAuiNotebook_GetPage, 2669).
+-define(wxAuiNotebook_GetPageBitmap, 2670).
+-define(wxAuiNotebook_GetPageCount, 2671).
+-define(wxAuiNotebook_GetPageIndex, 2672).
+-define(wxAuiNotebook_GetPageText, 2673).
+-define(wxAuiNotebook_GetSelection, 2674).
+-define(wxAuiNotebook_InsertPage, 2675).
+-define(wxAuiNotebook_RemovePage, 2676).
+-define(wxAuiNotebook_SetArtProvider, 2677).
+-define(wxAuiNotebook_SetFont, 2678).
+-define(wxAuiNotebook_SetPageBitmap, 2679).
+-define(wxAuiNotebook_SetPageText, 2680).
+-define(wxAuiNotebook_SetSelection, 2681).
+-define(wxAuiNotebook_SetTabCtrlHeight, 2682).
+-define(wxAuiNotebook_SetUniformBitmapSize, 2683).
+-define(wxAuiNotebook_destroy, 2684).
+-define(wxAuiTabArt_SetFlags, 2685).
+-define(wxAuiTabArt_SetMeasuringFont, 2686).
+-define(wxAuiTabArt_SetNormalFont, 2687).
+-define(wxAuiTabArt_SetSelectedFont, 2688).
+-define(wxAuiTabArt_SetColour, 2689).
+-define(wxAuiTabArt_SetActiveColour, 2690).
+-define(wxAuiDockArt_GetColour, 2691).
+-define(wxAuiDockArt_GetFont, 2692).
+-define(wxAuiDockArt_GetMetric, 2693).
+-define(wxAuiDockArt_SetColour, 2694).
+-define(wxAuiDockArt_SetFont, 2695).
+-define(wxAuiDockArt_SetMetric, 2696).
+-define(wxAuiSimpleTabArt_new, 2697).
+-define(wxAuiSimpleTabArt_destroy, 2698).
+-define(wxMDIParentFrame_new_0, 2699).
+-define(wxMDIParentFrame_new_4, 2700).
+-define(wxMDIParentFrame_destruct, 2701).
+-define(wxMDIParentFrame_ActivateNext, 2702).
+-define(wxMDIParentFrame_ActivatePrevious, 2703).
+-define(wxMDIParentFrame_ArrangeIcons, 2704).
+-define(wxMDIParentFrame_Cascade, 2705).
+-define(wxMDIParentFrame_Create, 2706).
+-define(wxMDIParentFrame_GetActiveChild, 2707).
+-define(wxMDIParentFrame_GetClientWindow, 2708).
+-define(wxMDIParentFrame_Tile, 2709).
+-define(wxMDIChildFrame_new_0, 2710).
+-define(wxMDIChildFrame_new_4, 2711).
+-define(wxMDIChildFrame_destruct, 2712).
+-define(wxMDIChildFrame_Activate, 2713).
+-define(wxMDIChildFrame_Create, 2714).
+-define(wxMDIChildFrame_Maximize, 2715).
+-define(wxMDIChildFrame_Restore, 2716).
+-define(wxMDIClientWindow_new_0, 2717).
+-define(wxMDIClientWindow_new_2, 2718).
+-define(wxMDIClientWindow_destruct, 2719).
+-define(wxMDIClientWindow_CreateClient, 2720).
+-define(wxLayoutAlgorithm_new, 2721).
+-define(wxLayoutAlgorithm_LayoutFrame, 2722).
+-define(wxLayoutAlgorithm_LayoutMDIFrame, 2723).
+-define(wxLayoutAlgorithm_LayoutWindow, 2724).
+-define(wxLayoutAlgorithm_destroy, 2725).
+-define(wxEvent_GetId, 2726).
+-define(wxEvent_GetSkipped, 2727).
+-define(wxEvent_GetTimestamp, 2728).
+-define(wxEvent_IsCommandEvent, 2729).
+-define(wxEvent_ResumePropagation, 2730).
+-define(wxEvent_ShouldPropagate, 2731).
+-define(wxEvent_Skip, 2732).
+-define(wxEvent_StopPropagation, 2733).
+-define(wxCommandEvent_getClientData, 2734).
+-define(wxCommandEvent_GetExtraLong, 2735).
+-define(wxCommandEvent_GetInt, 2736).
+-define(wxCommandEvent_GetSelection, 2737).
+-define(wxCommandEvent_GetString, 2738).
+-define(wxCommandEvent_IsChecked, 2739).
+-define(wxCommandEvent_IsSelection, 2740).
+-define(wxCommandEvent_SetInt, 2741).
+-define(wxCommandEvent_SetString, 2742).
+-define(wxScrollEvent_GetOrientation, 2743).
+-define(wxScrollEvent_GetPosition, 2744).
+-define(wxScrollWinEvent_GetOrientation, 2745).
+-define(wxScrollWinEvent_GetPosition, 2746).
+-define(wxMouseEvent_AltDown, 2747).
+-define(wxMouseEvent_Button, 2748).
+-define(wxMouseEvent_ButtonDClick, 2749).
+-define(wxMouseEvent_ButtonDown, 2750).
+-define(wxMouseEvent_ButtonUp, 2751).
+-define(wxMouseEvent_CmdDown, 2752).
+-define(wxMouseEvent_ControlDown, 2753).
+-define(wxMouseEvent_Dragging, 2754).
+-define(wxMouseEvent_Entering, 2755).
+-define(wxMouseEvent_GetButton, 2756).
+-define(wxMouseEvent_GetPosition, 2759).
+-define(wxMouseEvent_GetLogicalPosition, 2760).
+-define(wxMouseEvent_GetLinesPerAction, 2761).
+-define(wxMouseEvent_GetWheelRotation, 2762).
+-define(wxMouseEvent_GetWheelDelta, 2763).
+-define(wxMouseEvent_GetX, 2764).
+-define(wxMouseEvent_GetY, 2765).
+-define(wxMouseEvent_IsButton, 2766).
+-define(wxMouseEvent_IsPageScroll, 2767).
+-define(wxMouseEvent_Leaving, 2768).
+-define(wxMouseEvent_LeftDClick, 2769).
+-define(wxMouseEvent_LeftDown, 2770).
+-define(wxMouseEvent_LeftIsDown, 2771).
+-define(wxMouseEvent_LeftUp, 2772).
+-define(wxMouseEvent_MetaDown, 2773).
+-define(wxMouseEvent_MiddleDClick, 2774).
+-define(wxMouseEvent_MiddleDown, 2775).
+-define(wxMouseEvent_MiddleIsDown, 2776).
+-define(wxMouseEvent_MiddleUp, 2777).
+-define(wxMouseEvent_Moving, 2778).
+-define(wxMouseEvent_RightDClick, 2779).
+-define(wxMouseEvent_RightDown, 2780).
+-define(wxMouseEvent_RightIsDown, 2781).
+-define(wxMouseEvent_RightUp, 2782).
+-define(wxMouseEvent_ShiftDown, 2783).
+-define(wxSetCursorEvent_GetCursor, 2784).
+-define(wxSetCursorEvent_GetX, 2785).
+-define(wxSetCursorEvent_GetY, 2786).
+-define(wxSetCursorEvent_HasCursor, 2787).
+-define(wxSetCursorEvent_SetCursor, 2788).
+-define(wxKeyEvent_AltDown, 2789).
+-define(wxKeyEvent_CmdDown, 2790).
+-define(wxKeyEvent_ControlDown, 2791).
+-define(wxKeyEvent_GetKeyCode, 2792).
+-define(wxKeyEvent_GetModifiers, 2793).
+-define(wxKeyEvent_GetPosition, 2796).
+-define(wxKeyEvent_GetRawKeyCode, 2797).
+-define(wxKeyEvent_GetRawKeyFlags, 2798).
+-define(wxKeyEvent_GetUnicodeKey, 2799).
+-define(wxKeyEvent_GetX, 2800).
+-define(wxKeyEvent_GetY, 2801).
+-define(wxKeyEvent_HasModifiers, 2802).
+-define(wxKeyEvent_MetaDown, 2803).
+-define(wxKeyEvent_ShiftDown, 2804).
+-define(wxSizeEvent_GetSize, 2805).
+-define(wxMoveEvent_GetPosition, 2806).
+-define(wxEraseEvent_GetDC, 2807).
+-define(wxFocusEvent_GetWindow, 2808).
+-define(wxChildFocusEvent_GetWindow, 2809).
+-define(wxMenuEvent_GetMenu, 2810).
+-define(wxMenuEvent_GetMenuId, 2811).
+-define(wxMenuEvent_IsPopup, 2812).
+-define(wxCloseEvent_CanVeto, 2813).
+-define(wxCloseEvent_GetLoggingOff, 2814).
+-define(wxCloseEvent_SetCanVeto, 2815).
+-define(wxCloseEvent_SetLoggingOff, 2816).
+-define(wxCloseEvent_Veto, 2817).
+-define(wxShowEvent_SetShow, 2818).
+-define(wxShowEvent_GetShow, 2819).
+-define(wxIconizeEvent_Iconized, 2820).
+-define(wxJoystickEvent_ButtonDown, 2821).
+-define(wxJoystickEvent_ButtonIsDown, 2822).
+-define(wxJoystickEvent_ButtonUp, 2823).
+-define(wxJoystickEvent_GetButtonChange, 2824).
+-define(wxJoystickEvent_GetButtonState, 2825).
+-define(wxJoystickEvent_GetJoystick, 2826).
+-define(wxJoystickEvent_GetPosition, 2827).
+-define(wxJoystickEvent_GetZPosition, 2828).
+-define(wxJoystickEvent_IsButton, 2829).
+-define(wxJoystickEvent_IsMove, 2830).
+-define(wxJoystickEvent_IsZMove, 2831).
+-define(wxUpdateUIEvent_CanUpdate, 2832).
+-define(wxUpdateUIEvent_Check, 2833).
+-define(wxUpdateUIEvent_Enable, 2834).
+-define(wxUpdateUIEvent_Show, 2835).
+-define(wxUpdateUIEvent_GetChecked, 2836).
+-define(wxUpdateUIEvent_GetEnabled, 2837).
+-define(wxUpdateUIEvent_GetShown, 2838).
+-define(wxUpdateUIEvent_GetSetChecked, 2839).
+-define(wxUpdateUIEvent_GetSetEnabled, 2840).
+-define(wxUpdateUIEvent_GetSetShown, 2841).
+-define(wxUpdateUIEvent_GetSetText, 2842).
+-define(wxUpdateUIEvent_GetText, 2843).
+-define(wxUpdateUIEvent_GetMode, 2844).
+-define(wxUpdateUIEvent_GetUpdateInterval, 2845).
+-define(wxUpdateUIEvent_ResetUpdateTime, 2846).
+-define(wxUpdateUIEvent_SetMode, 2847).
+-define(wxUpdateUIEvent_SetText, 2848).
+-define(wxUpdateUIEvent_SetUpdateInterval, 2849).
+-define(wxMouseCaptureChangedEvent_GetCapturedWindow, 2850).
+-define(wxPaletteChangedEvent_SetChangedWindow, 2851).
+-define(wxPaletteChangedEvent_GetChangedWindow, 2852).
+-define(wxQueryNewPaletteEvent_SetPaletteRealized, 2853).
+-define(wxQueryNewPaletteEvent_GetPaletteRealized, 2854).
+-define(wxNavigationKeyEvent_GetDirection, 2855).
+-define(wxNavigationKeyEvent_SetDirection, 2856).
+-define(wxNavigationKeyEvent_IsWindowChange, 2857).
+-define(wxNavigationKeyEvent_SetWindowChange, 2858).
+-define(wxNavigationKeyEvent_IsFromTab, 2859).
+-define(wxNavigationKeyEvent_SetFromTab, 2860).
+-define(wxNavigationKeyEvent_GetCurrentFocus, 2861).
+-define(wxNavigationKeyEvent_SetCurrentFocus, 2862).
+-define(wxHelpEvent_GetOrigin, 2863).
+-define(wxHelpEvent_GetPosition, 2864).
+-define(wxHelpEvent_SetOrigin, 2865).
+-define(wxHelpEvent_SetPosition, 2866).
+-define(wxContextMenuEvent_GetPosition, 2867).
+-define(wxContextMenuEvent_SetPosition, 2868).
+-define(wxIdleEvent_CanSend, 2869).
+-define(wxIdleEvent_GetMode, 2870).
+-define(wxIdleEvent_RequestMore, 2871).
+-define(wxIdleEvent_MoreRequested, 2872).
+-define(wxIdleEvent_SetMode, 2873).
+-define(wxGridEvent_AltDown, 2874).
+-define(wxGridEvent_ControlDown, 2875).
+-define(wxGridEvent_GetCol, 2876).
+-define(wxGridEvent_GetPosition, 2877).
+-define(wxGridEvent_GetRow, 2878).
+-define(wxGridEvent_MetaDown, 2879).
+-define(wxGridEvent_Selecting, 2880).
+-define(wxGridEvent_ShiftDown, 2881).
+-define(wxNotifyEvent_Allow, 2882).
+-define(wxNotifyEvent_IsAllowed, 2883).
+-define(wxNotifyEvent_Veto, 2884).
+-define(wxSashEvent_GetEdge, 2885).
+-define(wxSashEvent_GetDragRect, 2886).
+-define(wxSashEvent_GetDragStatus, 2887).
+-define(wxListEvent_GetCacheFrom, 2888).
+-define(wxListEvent_GetCacheTo, 2889).
+-define(wxListEvent_GetKeyCode, 2890).
+-define(wxListEvent_GetIndex, 2891).
+-define(wxListEvent_GetColumn, 2892).
+-define(wxListEvent_GetPoint, 2893).
+-define(wxListEvent_GetLabel, 2894).
+-define(wxListEvent_GetText, 2895).
+-define(wxListEvent_GetImage, 2896).
+-define(wxListEvent_GetData, 2897).
+-define(wxListEvent_GetMask, 2898).
+-define(wxListEvent_GetItem, 2899).
+-define(wxListEvent_IsEditCancelled, 2900).
+-define(wxDateEvent_GetDate, 2901).
+-define(wxCalendarEvent_GetWeekDay, 2902).
+-define(wxFileDirPickerEvent_GetPath, 2903).
+-define(wxColourPickerEvent_GetColour, 2904).
+-define(wxFontPickerEvent_GetFont, 2905).
+-define(wxStyledTextEvent_GetPosition, 2906).
+-define(wxStyledTextEvent_GetKey, 2907).
+-define(wxStyledTextEvent_GetModifiers, 2908).
+-define(wxStyledTextEvent_GetModificationType, 2909).
+-define(wxStyledTextEvent_GetText, 2910).
+-define(wxStyledTextEvent_GetLength, 2911).
+-define(wxStyledTextEvent_GetLinesAdded, 2912).
+-define(wxStyledTextEvent_GetLine, 2913).
+-define(wxStyledTextEvent_GetFoldLevelNow, 2914).
+-define(wxStyledTextEvent_GetFoldLevelPrev, 2915).
+-define(wxStyledTextEvent_GetMargin, 2916).
+-define(wxStyledTextEvent_GetMessage, 2917).
+-define(wxStyledTextEvent_GetWParam, 2918).
+-define(wxStyledTextEvent_GetLParam, 2919).
+-define(wxStyledTextEvent_GetListType, 2920).
+-define(wxStyledTextEvent_GetX, 2921).
+-define(wxStyledTextEvent_GetY, 2922).
+-define(wxStyledTextEvent_GetDragText, 2923).
+-define(wxStyledTextEvent_GetDragAllowMove, 2924).
+-define(wxStyledTextEvent_GetDragResult, 2925).
+-define(wxStyledTextEvent_GetShift, 2926).
+-define(wxStyledTextEvent_GetControl, 2927).
+-define(wxStyledTextEvent_GetAlt, 2928).
+-define(utils_wxGetKeyState, 2929).
+-define(utils_wxGetMousePosition, 2930).
+-define(utils_wxGetMouseState, 2931).
+-define(utils_wxSetDetectableAutoRepeat, 2932).
+-define(utils_wxBell, 2933).
+-define(utils_wxFindMenuItemId, 2934).
+-define(utils_wxGenericFindWindowAtPoint, 2935).
+-define(utils_wxFindWindowAtPoint, 2936).
+-define(utils_wxBeginBusyCursor, 2937).
+-define(utils_wxEndBusyCursor, 2938).
+-define(utils_wxIsBusy, 2939).
+-define(utils_wxShutdown, 2940).
+-define(utils_wxShell, 2941).
+-define(utils_wxLaunchDefaultBrowser, 2942).
+-define(utils_wxGetEmailAddress, 2943).
+-define(utils_wxGetUserId, 2944).
+-define(utils_wxGetHomeDir, 2945).
+-define(utils_wxNewId, 2946).
+-define(utils_wxRegisterId, 2947).
+-define(utils_wxGetCurrentId, 2948).
+-define(utils_wxGetOsDescription, 2949).
+-define(utils_wxIsPlatformLittleEndian, 2950).
+-define(utils_wxIsPlatform64Bit, 2951).
+-define(gdicmn_wxDisplaySize, 2952).
+-define(gdicmn_wxSetCursor, 2953).
+-define(wxPrintout_new, 2954).
+-define(wxPrintout_destruct, 2955).
+-define(wxPrintout_GetDC, 2956).
+-define(wxPrintout_GetPageSizeMM, 2957).
+-define(wxPrintout_GetPageSizePixels, 2958).
+-define(wxPrintout_GetPaperRectPixels, 2959).
+-define(wxPrintout_GetPPIPrinter, 2960).
+-define(wxPrintout_GetPPIScreen, 2961).
+-define(wxPrintout_GetTitle, 2962).
+-define(wxPrintout_IsPreview, 2963).
+-define(wxPrintout_FitThisSizeToPaper, 2964).
+-define(wxPrintout_FitThisSizeToPage, 2965).
+-define(wxPrintout_FitThisSizeToPageMargins, 2966).
+-define(wxPrintout_MapScreenSizeToPaper, 2967).
+-define(wxPrintout_MapScreenSizeToPage, 2968).
+-define(wxPrintout_MapScreenSizeToPageMargins, 2969).
+-define(wxPrintout_MapScreenSizeToDevice, 2970).
+-define(wxPrintout_GetLogicalPaperRect, 2971).
+-define(wxPrintout_GetLogicalPageRect, 2972).
+-define(wxPrintout_GetLogicalPageMarginsRect, 2973).
+-define(wxPrintout_SetLogicalOrigin, 2974).
+-define(wxPrintout_OffsetLogicalOrigin, 2975).
+-define(wxStyledTextCtrl_new_2, 2976).
+-define(wxStyledTextCtrl_new_0, 2977).
+-define(wxStyledTextCtrl_destruct, 2978).
+-define(wxStyledTextCtrl_Create, 2979).
+-define(wxStyledTextCtrl_AddText, 2980).
+-define(wxStyledTextCtrl_AddStyledText, 2981).
+-define(wxStyledTextCtrl_InsertText, 2982).
+-define(wxStyledTextCtrl_ClearAll, 2983).
+-define(wxStyledTextCtrl_ClearDocumentStyle, 2984).
+-define(wxStyledTextCtrl_GetLength, 2985).
+-define(wxStyledTextCtrl_GetCharAt, 2986).
+-define(wxStyledTextCtrl_GetCurrentPos, 2987).
+-define(wxStyledTextCtrl_GetAnchor, 2988).
+-define(wxStyledTextCtrl_GetStyleAt, 2989).
+-define(wxStyledTextCtrl_Redo, 2990).
+-define(wxStyledTextCtrl_SetUndoCollection, 2991).
+-define(wxStyledTextCtrl_SelectAll, 2992).
+-define(wxStyledTextCtrl_SetSavePoint, 2993).
+-define(wxStyledTextCtrl_GetStyledText, 2994).
+-define(wxStyledTextCtrl_CanRedo, 2995).
+-define(wxStyledTextCtrl_MarkerLineFromHandle, 2996).
+-define(wxStyledTextCtrl_MarkerDeleteHandle, 2997).
+-define(wxStyledTextCtrl_GetUndoCollection, 2998).
+-define(wxStyledTextCtrl_GetViewWhiteSpace, 2999).
+-define(wxStyledTextCtrl_SetViewWhiteSpace, 3000).
+-define(wxStyledTextCtrl_PositionFromPoint, 3001).
+-define(wxStyledTextCtrl_PositionFromPointClose, 3002).
+-define(wxStyledTextCtrl_GotoLine, 3003).
+-define(wxStyledTextCtrl_GotoPos, 3004).
+-define(wxStyledTextCtrl_SetAnchor, 3005).
+-define(wxStyledTextCtrl_GetCurLine, 3006).
+-define(wxStyledTextCtrl_GetEndStyled, 3007).
+-define(wxStyledTextCtrl_ConvertEOLs, 3008).
+-define(wxStyledTextCtrl_GetEOLMode, 3009).
+-define(wxStyledTextCtrl_SetEOLMode, 3010).
+-define(wxStyledTextCtrl_StartStyling, 3011).
+-define(wxStyledTextCtrl_SetStyling, 3012).
+-define(wxStyledTextCtrl_GetBufferedDraw, 3013).
+-define(wxStyledTextCtrl_SetBufferedDraw, 3014).
+-define(wxStyledTextCtrl_SetTabWidth, 3015).
+-define(wxStyledTextCtrl_GetTabWidth, 3016).
+-define(wxStyledTextCtrl_SetCodePage, 3017).
+-define(wxStyledTextCtrl_MarkerDefine, 3018).
+-define(wxStyledTextCtrl_MarkerSetForeground, 3019).
+-define(wxStyledTextCtrl_MarkerSetBackground, 3020).
+-define(wxStyledTextCtrl_MarkerAdd, 3021).
+-define(wxStyledTextCtrl_MarkerDelete, 3022).
+-define(wxStyledTextCtrl_MarkerDeleteAll, 3023).
+-define(wxStyledTextCtrl_MarkerGet, 3024).
+-define(wxStyledTextCtrl_MarkerNext, 3025).
+-define(wxStyledTextCtrl_MarkerPrevious, 3026).
+-define(wxStyledTextCtrl_MarkerDefineBitmap, 3027).
+-define(wxStyledTextCtrl_MarkerAddSet, 3028).
+-define(wxStyledTextCtrl_MarkerSetAlpha, 3029).
+-define(wxStyledTextCtrl_SetMarginType, 3030).
+-define(wxStyledTextCtrl_GetMarginType, 3031).
+-define(wxStyledTextCtrl_SetMarginWidth, 3032).
+-define(wxStyledTextCtrl_GetMarginWidth, 3033).
+-define(wxStyledTextCtrl_SetMarginMask, 3034).
+-define(wxStyledTextCtrl_GetMarginMask, 3035).
+-define(wxStyledTextCtrl_SetMarginSensitive, 3036).
+-define(wxStyledTextCtrl_GetMarginSensitive, 3037).
+-define(wxStyledTextCtrl_StyleClearAll, 3038).
+-define(wxStyledTextCtrl_StyleSetForeground, 3039).
+-define(wxStyledTextCtrl_StyleSetBackground, 3040).
+-define(wxStyledTextCtrl_StyleSetBold, 3041).
+-define(wxStyledTextCtrl_StyleSetItalic, 3042).
+-define(wxStyledTextCtrl_StyleSetSize, 3043).
+-define(wxStyledTextCtrl_StyleSetFaceName, 3044).
+-define(wxStyledTextCtrl_StyleSetEOLFilled, 3045).
+-define(wxStyledTextCtrl_StyleResetDefault, 3046).
+-define(wxStyledTextCtrl_StyleSetUnderline, 3047).
+-define(wxStyledTextCtrl_StyleSetCase, 3048).
+-define(wxStyledTextCtrl_StyleSetHotSpot, 3049).
+-define(wxStyledTextCtrl_SetSelForeground, 3050).
+-define(wxStyledTextCtrl_SetSelBackground, 3051).
+-define(wxStyledTextCtrl_GetSelAlpha, 3052).
+-define(wxStyledTextCtrl_SetSelAlpha, 3053).
+-define(wxStyledTextCtrl_SetCaretForeground, 3054).
+-define(wxStyledTextCtrl_CmdKeyAssign, 3055).
+-define(wxStyledTextCtrl_CmdKeyClear, 3056).
+-define(wxStyledTextCtrl_CmdKeyClearAll, 3057).
+-define(wxStyledTextCtrl_SetStyleBytes, 3058).
+-define(wxStyledTextCtrl_StyleSetVisible, 3059).
+-define(wxStyledTextCtrl_GetCaretPeriod, 3060).
+-define(wxStyledTextCtrl_SetCaretPeriod, 3061).
+-define(wxStyledTextCtrl_SetWordChars, 3062).
+-define(wxStyledTextCtrl_BeginUndoAction, 3063).
+-define(wxStyledTextCtrl_EndUndoAction, 3064).
+-define(wxStyledTextCtrl_IndicatorSetStyle, 3065).
+-define(wxStyledTextCtrl_IndicatorGetStyle, 3066).
+-define(wxStyledTextCtrl_IndicatorSetForeground, 3067).
+-define(wxStyledTextCtrl_IndicatorGetForeground, 3068).
+-define(wxStyledTextCtrl_SetWhitespaceForeground, 3069).
+-define(wxStyledTextCtrl_SetWhitespaceBackground, 3070).
+-define(wxStyledTextCtrl_GetStyleBits, 3071).
+-define(wxStyledTextCtrl_SetLineState, 3072).
+-define(wxStyledTextCtrl_GetLineState, 3073).
+-define(wxStyledTextCtrl_GetMaxLineState, 3074).
+-define(wxStyledTextCtrl_GetCaretLineVisible, 3075).
+-define(wxStyledTextCtrl_SetCaretLineVisible, 3076).
+-define(wxStyledTextCtrl_GetCaretLineBackground, 3077).
+-define(wxStyledTextCtrl_SetCaretLineBackground, 3078).
+-define(wxStyledTextCtrl_AutoCompShow, 3079).
+-define(wxStyledTextCtrl_AutoCompCancel, 3080).
+-define(wxStyledTextCtrl_AutoCompActive, 3081).
+-define(wxStyledTextCtrl_AutoCompPosStart, 3082).
+-define(wxStyledTextCtrl_AutoCompComplete, 3083).
+-define(wxStyledTextCtrl_AutoCompStops, 3084).
+-define(wxStyledTextCtrl_AutoCompSetSeparator, 3085).
+-define(wxStyledTextCtrl_AutoCompGetSeparator, 3086).
+-define(wxStyledTextCtrl_AutoCompSelect, 3087).
+-define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3088).
+-define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3089).
+-define(wxStyledTextCtrl_AutoCompSetFillUps, 3090).
+-define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3091).
+-define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3092).
+-define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3093).
+-define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3094).
+-define(wxStyledTextCtrl_UserListShow, 3095).
+-define(wxStyledTextCtrl_AutoCompSetAutoHide, 3096).
+-define(wxStyledTextCtrl_AutoCompGetAutoHide, 3097).
+-define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3098).
+-define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3099).
+-define(wxStyledTextCtrl_RegisterImage, 3100).
+-define(wxStyledTextCtrl_ClearRegisteredImages, 3101).
+-define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3102).
+-define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3103).
+-define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3104).
+-define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3105).
+-define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3106).
+-define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3107).
+-define(wxStyledTextCtrl_SetIndent, 3108).
+-define(wxStyledTextCtrl_GetIndent, 3109).
+-define(wxStyledTextCtrl_SetUseTabs, 3110).
+-define(wxStyledTextCtrl_GetUseTabs, 3111).
+-define(wxStyledTextCtrl_SetLineIndentation, 3112).
+-define(wxStyledTextCtrl_GetLineIndentation, 3113).
+-define(wxStyledTextCtrl_GetLineIndentPosition, 3114).
+-define(wxStyledTextCtrl_GetColumn, 3115).
+-define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3116).
+-define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3117).
+-define(wxStyledTextCtrl_SetIndentationGuides, 3118).
+-define(wxStyledTextCtrl_GetIndentationGuides, 3119).
+-define(wxStyledTextCtrl_SetHighlightGuide, 3120).
+-define(wxStyledTextCtrl_GetHighlightGuide, 3121).
+-define(wxStyledTextCtrl_GetLineEndPosition, 3122).
+-define(wxStyledTextCtrl_GetCodePage, 3123).
+-define(wxStyledTextCtrl_GetCaretForeground, 3124).
+-define(wxStyledTextCtrl_GetReadOnly, 3125).
+-define(wxStyledTextCtrl_SetCurrentPos, 3126).
+-define(wxStyledTextCtrl_SetSelectionStart, 3127).
+-define(wxStyledTextCtrl_GetSelectionStart, 3128).
+-define(wxStyledTextCtrl_SetSelectionEnd, 3129).
+-define(wxStyledTextCtrl_GetSelectionEnd, 3130).
+-define(wxStyledTextCtrl_SetPrintMagnification, 3131).
+-define(wxStyledTextCtrl_GetPrintMagnification, 3132).
+-define(wxStyledTextCtrl_SetPrintColourMode, 3133).
+-define(wxStyledTextCtrl_GetPrintColourMode, 3134).
+-define(wxStyledTextCtrl_FindText, 3135).
+-define(wxStyledTextCtrl_FormatRange, 3136).
+-define(wxStyledTextCtrl_GetFirstVisibleLine, 3137).
+-define(wxStyledTextCtrl_GetLine, 3138).
+-define(wxStyledTextCtrl_GetLineCount, 3139).
+-define(wxStyledTextCtrl_SetMarginLeft, 3140).
+-define(wxStyledTextCtrl_GetMarginLeft, 3141).
+-define(wxStyledTextCtrl_SetMarginRight, 3142).
+-define(wxStyledTextCtrl_GetMarginRight, 3143).
+-define(wxStyledTextCtrl_GetModify, 3144).
+-define(wxStyledTextCtrl_SetSelection, 3145).
+-define(wxStyledTextCtrl_GetSelectedText, 3146).
+-define(wxStyledTextCtrl_GetTextRange, 3147).
+-define(wxStyledTextCtrl_HideSelection, 3148).
+-define(wxStyledTextCtrl_LineFromPosition, 3149).
+-define(wxStyledTextCtrl_PositionFromLine, 3150).
+-define(wxStyledTextCtrl_LineScroll, 3151).
+-define(wxStyledTextCtrl_EnsureCaretVisible, 3152).
+-define(wxStyledTextCtrl_ReplaceSelection, 3153).
+-define(wxStyledTextCtrl_SetReadOnly, 3154).
+-define(wxStyledTextCtrl_CanPaste, 3155).
+-define(wxStyledTextCtrl_CanUndo, 3156).
+-define(wxStyledTextCtrl_EmptyUndoBuffer, 3157).
+-define(wxStyledTextCtrl_Undo, 3158).
+-define(wxStyledTextCtrl_Cut, 3159).
+-define(wxStyledTextCtrl_Copy, 3160).
+-define(wxStyledTextCtrl_Paste, 3161).
+-define(wxStyledTextCtrl_Clear, 3162).
+-define(wxStyledTextCtrl_SetText, 3163).
+-define(wxStyledTextCtrl_GetText, 3164).
+-define(wxStyledTextCtrl_GetTextLength, 3165).
+-define(wxStyledTextCtrl_GetOvertype, 3166).
+-define(wxStyledTextCtrl_SetCaretWidth, 3167).
+-define(wxStyledTextCtrl_GetCaretWidth, 3168).
+-define(wxStyledTextCtrl_SetTargetStart, 3169).
+-define(wxStyledTextCtrl_GetTargetStart, 3170).
+-define(wxStyledTextCtrl_SetTargetEnd, 3171).
+-define(wxStyledTextCtrl_GetTargetEnd, 3172).
+-define(wxStyledTextCtrl_ReplaceTarget, 3173).
+-define(wxStyledTextCtrl_SearchInTarget, 3174).
+-define(wxStyledTextCtrl_SetSearchFlags, 3175).
+-define(wxStyledTextCtrl_GetSearchFlags, 3176).
+-define(wxStyledTextCtrl_CallTipShow, 3177).
+-define(wxStyledTextCtrl_CallTipCancel, 3178).
+-define(wxStyledTextCtrl_CallTipActive, 3179).
+-define(wxStyledTextCtrl_CallTipPosAtStart, 3180).
+-define(wxStyledTextCtrl_CallTipSetHighlight, 3181).
+-define(wxStyledTextCtrl_CallTipSetBackground, 3182).
+-define(wxStyledTextCtrl_CallTipSetForeground, 3183).
+-define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3184).
+-define(wxStyledTextCtrl_CallTipUseStyle, 3185).
+-define(wxStyledTextCtrl_VisibleFromDocLine, 3186).
+-define(wxStyledTextCtrl_DocLineFromVisible, 3187).
+-define(wxStyledTextCtrl_WrapCount, 3188).
+-define(wxStyledTextCtrl_SetFoldLevel, 3189).
+-define(wxStyledTextCtrl_GetFoldLevel, 3190).
+-define(wxStyledTextCtrl_GetLastChild, 3191).
+-define(wxStyledTextCtrl_GetFoldParent, 3192).
+-define(wxStyledTextCtrl_ShowLines, 3193).
+-define(wxStyledTextCtrl_HideLines, 3194).
+-define(wxStyledTextCtrl_GetLineVisible, 3195).
+-define(wxStyledTextCtrl_SetFoldExpanded, 3196).
+-define(wxStyledTextCtrl_GetFoldExpanded, 3197).
+-define(wxStyledTextCtrl_ToggleFold, 3198).
+-define(wxStyledTextCtrl_EnsureVisible, 3199).
+-define(wxStyledTextCtrl_SetFoldFlags, 3200).
+-define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3201).
+-define(wxStyledTextCtrl_SetTabIndents, 3202).
+-define(wxStyledTextCtrl_GetTabIndents, 3203).
+-define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3204).
+-define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3205).
+-define(wxStyledTextCtrl_SetMouseDwellTime, 3206).
+-define(wxStyledTextCtrl_GetMouseDwellTime, 3207).
+-define(wxStyledTextCtrl_WordStartPosition, 3208).
+-define(wxStyledTextCtrl_WordEndPosition, 3209).
+-define(wxStyledTextCtrl_SetWrapMode, 3210).
+-define(wxStyledTextCtrl_GetWrapMode, 3211).
+-define(wxStyledTextCtrl_SetWrapVisualFlags, 3212).
+-define(wxStyledTextCtrl_GetWrapVisualFlags, 3213).
+-define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3214).
+-define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3215).
+-define(wxStyledTextCtrl_SetWrapStartIndent, 3216).
+-define(wxStyledTextCtrl_GetWrapStartIndent, 3217).
+-define(wxStyledTextCtrl_SetLayoutCache, 3218).
+-define(wxStyledTextCtrl_GetLayoutCache, 3219).
+-define(wxStyledTextCtrl_SetScrollWidth, 3220).
+-define(wxStyledTextCtrl_GetScrollWidth, 3221).
+-define(wxStyledTextCtrl_TextWidth, 3222).
+-define(wxStyledTextCtrl_GetEndAtLastLine, 3223).
+-define(wxStyledTextCtrl_TextHeight, 3224).
+-define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3225).
+-define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3226).
+-define(wxStyledTextCtrl_AppendText, 3227).
+-define(wxStyledTextCtrl_GetTwoPhaseDraw, 3228).
+-define(wxStyledTextCtrl_SetTwoPhaseDraw, 3229).
+-define(wxStyledTextCtrl_TargetFromSelection, 3230).
+-define(wxStyledTextCtrl_LinesJoin, 3231).
+-define(wxStyledTextCtrl_LinesSplit, 3232).
+-define(wxStyledTextCtrl_SetFoldMarginColour, 3233).
+-define(wxStyledTextCtrl_SetFoldMarginHiColour, 3234).
+-define(wxStyledTextCtrl_LineDown, 3235).
+-define(wxStyledTextCtrl_LineDownExtend, 3236).
+-define(wxStyledTextCtrl_LineUp, 3237).
+-define(wxStyledTextCtrl_LineUpExtend, 3238).
+-define(wxStyledTextCtrl_CharLeft, 3239).
+-define(wxStyledTextCtrl_CharLeftExtend, 3240).
+-define(wxStyledTextCtrl_CharRight, 3241).
+-define(wxStyledTextCtrl_CharRightExtend, 3242).
+-define(wxStyledTextCtrl_WordLeft, 3243).
+-define(wxStyledTextCtrl_WordLeftExtend, 3244).
+-define(wxStyledTextCtrl_WordRight, 3245).
+-define(wxStyledTextCtrl_WordRightExtend, 3246).
+-define(wxStyledTextCtrl_Home, 3247).
+-define(wxStyledTextCtrl_HomeExtend, 3248).
+-define(wxStyledTextCtrl_LineEnd, 3249).
+-define(wxStyledTextCtrl_LineEndExtend, 3250).
+-define(wxStyledTextCtrl_DocumentStart, 3251).
+-define(wxStyledTextCtrl_DocumentStartExtend, 3252).
+-define(wxStyledTextCtrl_DocumentEnd, 3253).
+-define(wxStyledTextCtrl_DocumentEndExtend, 3254).
+-define(wxStyledTextCtrl_PageUp, 3255).
+-define(wxStyledTextCtrl_PageUpExtend, 3256).
+-define(wxStyledTextCtrl_PageDown, 3257).
+-define(wxStyledTextCtrl_PageDownExtend, 3258).
+-define(wxStyledTextCtrl_EditToggleOvertype, 3259).
+-define(wxStyledTextCtrl_Cancel, 3260).
+-define(wxStyledTextCtrl_DeleteBack, 3261).
+-define(wxStyledTextCtrl_Tab, 3262).
+-define(wxStyledTextCtrl_BackTab, 3263).
+-define(wxStyledTextCtrl_NewLine, 3264).
+-define(wxStyledTextCtrl_FormFeed, 3265).
+-define(wxStyledTextCtrl_VCHome, 3266).
+-define(wxStyledTextCtrl_VCHomeExtend, 3267).
+-define(wxStyledTextCtrl_ZoomIn, 3268).
+-define(wxStyledTextCtrl_ZoomOut, 3269).
+-define(wxStyledTextCtrl_DelWordLeft, 3270).
+-define(wxStyledTextCtrl_DelWordRight, 3271).
+-define(wxStyledTextCtrl_LineCut, 3272).
+-define(wxStyledTextCtrl_LineDelete, 3273).
+-define(wxStyledTextCtrl_LineTranspose, 3274).
+-define(wxStyledTextCtrl_LineDuplicate, 3275).
+-define(wxStyledTextCtrl_LowerCase, 3276).
+-define(wxStyledTextCtrl_UpperCase, 3277).
+-define(wxStyledTextCtrl_LineScrollDown, 3278).
+-define(wxStyledTextCtrl_LineScrollUp, 3279).
+-define(wxStyledTextCtrl_DeleteBackNotLine, 3280).
+-define(wxStyledTextCtrl_HomeDisplay, 3281).
+-define(wxStyledTextCtrl_HomeDisplayExtend, 3282).
+-define(wxStyledTextCtrl_LineEndDisplay, 3283).
+-define(wxStyledTextCtrl_LineEndDisplayExtend, 3284).
+-define(wxStyledTextCtrl_HomeWrapExtend, 3285).
+-define(wxStyledTextCtrl_LineEndWrap, 3286).
+-define(wxStyledTextCtrl_LineEndWrapExtend, 3287).
+-define(wxStyledTextCtrl_VCHomeWrap, 3288).
+-define(wxStyledTextCtrl_VCHomeWrapExtend, 3289).
+-define(wxStyledTextCtrl_LineCopy, 3290).
+-define(wxStyledTextCtrl_MoveCaretInsideView, 3291).
+-define(wxStyledTextCtrl_LineLength, 3292).
+-define(wxStyledTextCtrl_BraceHighlight, 3293).
+-define(wxStyledTextCtrl_BraceBadLight, 3294).
+-define(wxStyledTextCtrl_BraceMatch, 3295).
+-define(wxStyledTextCtrl_GetViewEOL, 3296).
+-define(wxStyledTextCtrl_SetViewEOL, 3297).
+-define(wxStyledTextCtrl_SetModEventMask, 3298).
+-define(wxStyledTextCtrl_GetEdgeColumn, 3299).
+-define(wxStyledTextCtrl_SetEdgeColumn, 3300).
+-define(wxStyledTextCtrl_SetEdgeMode, 3301).
+-define(wxStyledTextCtrl_GetEdgeMode, 3302).
+-define(wxStyledTextCtrl_GetEdgeColour, 3303).
+-define(wxStyledTextCtrl_SetEdgeColour, 3304).
+-define(wxStyledTextCtrl_SearchAnchor, 3305).
+-define(wxStyledTextCtrl_SearchNext, 3306).
+-define(wxStyledTextCtrl_SearchPrev, 3307).
+-define(wxStyledTextCtrl_LinesOnScreen, 3308).
+-define(wxStyledTextCtrl_UsePopUp, 3309).
+-define(wxStyledTextCtrl_SelectionIsRectangle, 3310).
+-define(wxStyledTextCtrl_SetZoom, 3311).
+-define(wxStyledTextCtrl_GetZoom, 3312).
+-define(wxStyledTextCtrl_GetModEventMask, 3313).
+-define(wxStyledTextCtrl_SetSTCFocus, 3314).
+-define(wxStyledTextCtrl_GetSTCFocus, 3315).
+-define(wxStyledTextCtrl_SetStatus, 3316).
+-define(wxStyledTextCtrl_GetStatus, 3317).
+-define(wxStyledTextCtrl_SetMouseDownCaptures, 3318).
+-define(wxStyledTextCtrl_GetMouseDownCaptures, 3319).
+-define(wxStyledTextCtrl_SetSTCCursor, 3320).
+-define(wxStyledTextCtrl_GetSTCCursor, 3321).
+-define(wxStyledTextCtrl_SetControlCharSymbol, 3322).
+-define(wxStyledTextCtrl_GetControlCharSymbol, 3323).
+-define(wxStyledTextCtrl_WordPartLeft, 3324).
+-define(wxStyledTextCtrl_WordPartLeftExtend, 3325).
+-define(wxStyledTextCtrl_WordPartRight, 3326).
+-define(wxStyledTextCtrl_WordPartRightExtend, 3327).
+-define(wxStyledTextCtrl_SetVisiblePolicy, 3328).
+-define(wxStyledTextCtrl_DelLineLeft, 3329).
+-define(wxStyledTextCtrl_DelLineRight, 3330).
+-define(wxStyledTextCtrl_GetXOffset, 3331).
+-define(wxStyledTextCtrl_ChooseCaretX, 3332).
+-define(wxStyledTextCtrl_SetXCaretPolicy, 3333).
+-define(wxStyledTextCtrl_SetYCaretPolicy, 3334).
+-define(wxStyledTextCtrl_GetPrintWrapMode, 3335).
+-define(wxStyledTextCtrl_SetHotspotActiveForeground, 3336).
+-define(wxStyledTextCtrl_SetHotspotActiveBackground, 3337).
+-define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3338).
+-define(wxStyledTextCtrl_SetHotspotSingleLine, 3339).
+-define(wxStyledTextCtrl_ParaDownExtend, 3340).
+-define(wxStyledTextCtrl_ParaUp, 3341).
+-define(wxStyledTextCtrl_ParaUpExtend, 3342).
+-define(wxStyledTextCtrl_PositionBefore, 3343).
+-define(wxStyledTextCtrl_PositionAfter, 3344).
+-define(wxStyledTextCtrl_CopyRange, 3345).
+-define(wxStyledTextCtrl_CopyText, 3346).
+-define(wxStyledTextCtrl_SetSelectionMode, 3347).
+-define(wxStyledTextCtrl_GetSelectionMode, 3348).
+-define(wxStyledTextCtrl_LineDownRectExtend, 3349).
+-define(wxStyledTextCtrl_LineUpRectExtend, 3350).
+-define(wxStyledTextCtrl_CharLeftRectExtend, 3351).
+-define(wxStyledTextCtrl_CharRightRectExtend, 3352).
+-define(wxStyledTextCtrl_HomeRectExtend, 3353).
+-define(wxStyledTextCtrl_VCHomeRectExtend, 3354).
+-define(wxStyledTextCtrl_LineEndRectExtend, 3355).
+-define(wxStyledTextCtrl_PageUpRectExtend, 3356).
+-define(wxStyledTextCtrl_PageDownRectExtend, 3357).
+-define(wxStyledTextCtrl_StutteredPageUp, 3358).
+-define(wxStyledTextCtrl_StutteredPageUpExtend, 3359).
+-define(wxStyledTextCtrl_StutteredPageDown, 3360).
+-define(wxStyledTextCtrl_StutteredPageDownExtend, 3361).
+-define(wxStyledTextCtrl_WordLeftEnd, 3362).
+-define(wxStyledTextCtrl_WordLeftEndExtend, 3363).
+-define(wxStyledTextCtrl_WordRightEnd, 3364).
+-define(wxStyledTextCtrl_WordRightEndExtend, 3365).
+-define(wxStyledTextCtrl_SetWhitespaceChars, 3366).
+-define(wxStyledTextCtrl_SetCharsDefault, 3367).
+-define(wxStyledTextCtrl_AutoCompGetCurrent, 3368).
+-define(wxStyledTextCtrl_Allocate, 3369).
+-define(wxStyledTextCtrl_FindColumn, 3370).
+-define(wxStyledTextCtrl_GetCaretSticky, 3371).
+-define(wxStyledTextCtrl_SetCaretSticky, 3372).
+-define(wxStyledTextCtrl_ToggleCaretSticky, 3373).
+-define(wxStyledTextCtrl_SetPasteConvertEndings, 3374).
+-define(wxStyledTextCtrl_GetPasteConvertEndings, 3375).
+-define(wxStyledTextCtrl_SelectionDuplicate, 3376).
+-define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3377).
+-define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3378).
+-define(wxStyledTextCtrl_StartRecord, 3379).
+-define(wxStyledTextCtrl_StopRecord, 3380).
+-define(wxStyledTextCtrl_SetLexer, 3381).
+-define(wxStyledTextCtrl_GetLexer, 3382).
+-define(wxStyledTextCtrl_Colourise, 3383).
+-define(wxStyledTextCtrl_SetProperty, 3384).
+-define(wxStyledTextCtrl_SetKeyWords, 3385).
+-define(wxStyledTextCtrl_SetLexerLanguage, 3386).
+-define(wxStyledTextCtrl_GetProperty, 3387).
+-define(wxStyledTextCtrl_GetStyleBitsNeeded, 3388).
+-define(wxStyledTextCtrl_GetCurrentLine, 3389).
+-define(wxStyledTextCtrl_StyleSetSpec, 3390).
+-define(wxStyledTextCtrl_StyleSetFont, 3391).
+-define(wxStyledTextCtrl_StyleSetFontAttr, 3392).
+-define(wxStyledTextCtrl_StyleSetCharacterSet, 3393).
+-define(wxStyledTextCtrl_StyleSetFontEncoding, 3394).
+-define(wxStyledTextCtrl_CmdKeyExecute, 3395).
+-define(wxStyledTextCtrl_SetMargins, 3396).
+-define(wxStyledTextCtrl_GetSelection, 3397).
+-define(wxStyledTextCtrl_PointFromPosition, 3398).
+-define(wxStyledTextCtrl_ScrollToLine, 3399).
+-define(wxStyledTextCtrl_ScrollToColumn, 3400).
+-define(wxStyledTextCtrl_SetVScrollBar, 3401).
+-define(wxStyledTextCtrl_SetHScrollBar, 3402).
+-define(wxStyledTextCtrl_GetLastKeydownProcessed, 3403).
+-define(wxStyledTextCtrl_SetLastKeydownProcessed, 3404).
+-define(wxStyledTextCtrl_SaveFile, 3405).
+-define(wxStyledTextCtrl_LoadFile, 3406).
+-define(wxStyledTextCtrl_DoDragOver, 3407).
+-define(wxStyledTextCtrl_DoDropText, 3408).
+-define(wxStyledTextCtrl_GetUseAntiAliasing, 3409).
+-define(wxStyledTextCtrl_AddTextRaw, 3410).
+-define(wxStyledTextCtrl_InsertTextRaw, 3411).
+-define(wxStyledTextCtrl_GetCurLineRaw, 3412).
+-define(wxStyledTextCtrl_GetLineRaw, 3413).
+-define(wxStyledTextCtrl_GetSelectedTextRaw, 3414).
+-define(wxStyledTextCtrl_GetTextRangeRaw, 3415).
+-define(wxStyledTextCtrl_SetTextRaw, 3416).
+-define(wxStyledTextCtrl_GetTextRaw, 3417).
+-define(wxStyledTextCtrl_AppendTextRaw, 3418).
+-define(wxArtProvider_GetBitmap, 3419).
+-define(wxArtProvider_GetIcon, 3420).
+-define(wxTreeEvent_GetKeyCode, 3421).
+-define(wxTreeEvent_GetItem, 3422).
+-define(wxTreeEvent_GetKeyEvent, 3423).
+-define(wxTreeEvent_GetLabel, 3424).
+-define(wxTreeEvent_GetOldItem, 3425).
+-define(wxTreeEvent_GetPoint, 3426).
+-define(wxTreeEvent_IsEditCancelled, 3427).
+-define(wxTreeEvent_SetToolTip, 3428).
+-define(wxNotebookEvent_GetOldSelection, 3429).
+-define(wxNotebookEvent_GetSelection, 3430).
+-define(wxNotebookEvent_SetOldSelection, 3431).
+-define(wxNotebookEvent_SetSelection, 3432).
+-define(wxFileDataObject_new, 3433).
+-define(wxFileDataObject_AddFile, 3434).
+-define(wxFileDataObject_GetFilenames, 3435).
+-define(wxFileDataObject_destroy, 3436).
+-define(wxTextDataObject_new, 3437).
+-define(wxTextDataObject_GetTextLength, 3438).
+-define(wxTextDataObject_GetText, 3439).
+-define(wxTextDataObject_SetText, 3440).
+-define(wxTextDataObject_destroy, 3441).
+-define(wxBitmapDataObject_new_1_1, 3442).
+-define(wxBitmapDataObject_new_1_0, 3443).
+-define(wxBitmapDataObject_GetBitmap, 3444).
+-define(wxBitmapDataObject_SetBitmap, 3445).
+-define(wxBitmapDataObject_destroy, 3446).
+-define(wxClipboard_new, 3448).
+-define(wxClipboard_destruct, 3449).
+-define(wxClipboard_AddData, 3450).
+-define(wxClipboard_Clear, 3451).
+-define(wxClipboard_Close, 3452).
+-define(wxClipboard_Flush, 3453).
+-define(wxClipboard_GetData, 3454).
+-define(wxClipboard_IsOpened, 3455).
+-define(wxClipboard_Open, 3456).
+-define(wxClipboard_SetData, 3457).
+-define(wxClipboard_UsePrimarySelection, 3459).
+-define(wxClipboard_IsSupported, 3460).
+-define(wxClipboard_Get, 3461).
+-define(wxSpinEvent_GetPosition, 3462).
+-define(wxSpinEvent_SetPosition, 3463).
+-define(wxSplitterWindow_new_0, 3464).
+-define(wxSplitterWindow_new_2, 3465).
+-define(wxSplitterWindow_destruct, 3466).
+-define(wxSplitterWindow_Create, 3467).
+-define(wxSplitterWindow_GetMinimumPaneSize, 3468).
+-define(wxSplitterWindow_GetSashGravity, 3469).
+-define(wxSplitterWindow_GetSashPosition, 3470).
+-define(wxSplitterWindow_GetSplitMode, 3471).
+-define(wxSplitterWindow_GetWindow1, 3472).
+-define(wxSplitterWindow_GetWindow2, 3473).
+-define(wxSplitterWindow_Initialize, 3474).
+-define(wxSplitterWindow_IsSplit, 3475).
+-define(wxSplitterWindow_ReplaceWindow, 3476).
+-define(wxSplitterWindow_SetSashGravity, 3477).
+-define(wxSplitterWindow_SetSashPosition, 3478).
+-define(wxSplitterWindow_SetSashSize, 3479).
+-define(wxSplitterWindow_SetMinimumPaneSize, 3480).
+-define(wxSplitterWindow_SetSplitMode, 3481).
+-define(wxSplitterWindow_SplitHorizontally, 3482).
+-define(wxSplitterWindow_SplitVertically, 3483).
+-define(wxSplitterWindow_Unsplit, 3484).
+-define(wxSplitterWindow_UpdateSize, 3485).
+-define(wxSplitterEvent_GetSashPosition, 3486).
+-define(wxSplitterEvent_GetX, 3487).
+-define(wxSplitterEvent_GetY, 3488).
+-define(wxSplitterEvent_GetWindowBeingRemoved, 3489).
+-define(wxSplitterEvent_SetSashPosition, 3490).
+-define(wxHtmlWindow_new_0, 3491).
+-define(wxHtmlWindow_new_2, 3492).
+-define(wxHtmlWindow_AppendToPage, 3493).
+-define(wxHtmlWindow_GetOpenedAnchor, 3494).
+-define(wxHtmlWindow_GetOpenedPage, 3495).
+-define(wxHtmlWindow_GetOpenedPageTitle, 3496).
+-define(wxHtmlWindow_GetRelatedFrame, 3497).
+-define(wxHtmlWindow_HistoryBack, 3498).
+-define(wxHtmlWindow_HistoryCanBack, 3499).
+-define(wxHtmlWindow_HistoryCanForward, 3500).
+-define(wxHtmlWindow_HistoryClear, 3501).
+-define(wxHtmlWindow_HistoryForward, 3502).
+-define(wxHtmlWindow_LoadFile, 3503).
+-define(wxHtmlWindow_LoadPage, 3504).
+-define(wxHtmlWindow_SelectAll, 3505).
+-define(wxHtmlWindow_SelectionToText, 3506).
+-define(wxHtmlWindow_SelectLine, 3507).
+-define(wxHtmlWindow_SelectWord, 3508).
+-define(wxHtmlWindow_SetBorders, 3509).
+-define(wxHtmlWindow_SetFonts, 3510).
+-define(wxHtmlWindow_SetPage, 3511).
+-define(wxHtmlWindow_SetRelatedFrame, 3512).
+-define(wxHtmlWindow_SetRelatedStatusBar, 3513).
+-define(wxHtmlWindow_ToText, 3514).
+-define(wxHtmlWindow_destroy, 3515).
+-define(wxHtmlLinkEvent_GetLinkInfo, 3516).
+-define(wxSystemSettings_GetColour, 3517).
+-define(wxSystemSettings_GetFont, 3518).
+-define(wxSystemSettings_GetMetric, 3519).
+-define(wxSystemSettings_GetScreenType, 3520).
+-define(wxSystemOptions_GetOption, 3521).
+-define(wxSystemOptions_GetOptionInt, 3522).
+-define(wxSystemOptions_HasOption, 3523).
+-define(wxSystemOptions_IsFalse, 3524).
+-define(wxSystemOptions_SetOption_2_1, 3525).
+-define(wxSystemOptions_SetOption_2_0, 3526).
+-define(wxAuiNotebookEvent_SetSelection, 3527).
+-define(wxAuiNotebookEvent_GetSelection, 3528).
+-define(wxAuiNotebookEvent_SetOldSelection, 3529).
+-define(wxAuiNotebookEvent_GetOldSelection, 3530).
+-define(wxAuiNotebookEvent_SetDragSource, 3531).
+-define(wxAuiNotebookEvent_GetDragSource, 3532).
+-define(wxAuiManagerEvent_SetManager, 3533).
+-define(wxAuiManagerEvent_GetManager, 3534).
+-define(wxAuiManagerEvent_SetPane, 3535).
+-define(wxAuiManagerEvent_GetPane, 3536).
+-define(wxAuiManagerEvent_SetButton, 3537).
+-define(wxAuiManagerEvent_GetButton, 3538).
+-define(wxAuiManagerEvent_SetDC, 3539).
+-define(wxAuiManagerEvent_GetDC, 3540).
+-define(wxAuiManagerEvent_Veto, 3541).
+-define(wxAuiManagerEvent_GetVeto, 3542).
+-define(wxAuiManagerEvent_SetCanVeto, 3543).
+-define(wxAuiManagerEvent_CanVeto, 3544).
+-define(wxLogNull_new, 3545).
+-define(wxLogNull_destroy, 3546).
+-define(wxTaskBarIcon_new, 3547).
+-define(wxTaskBarIcon_destruct, 3548).
+-define(wxTaskBarIcon_PopupMenu, 3549).
+-define(wxTaskBarIcon_RemoveIcon, 3550).
+-define(wxTaskBarIcon_SetIcon, 3551).
+-define(wxLocale_new_0, 3552).
+-define(wxLocale_new_2, 3554).
+-define(wxLocale_destruct, 3555).
+-define(wxLocale_Init, 3557).
+-define(wxLocale_AddCatalog_1, 3558).
+-define(wxLocale_AddCatalog_3, 3559).
+-define(wxLocale_AddCatalogLookupPathPrefix, 3560).
+-define(wxLocale_GetCanonicalName, 3561).
+-define(wxLocale_GetLanguage, 3562).
+-define(wxLocale_GetLanguageName, 3563).
+-define(wxLocale_GetLocale, 3564).
+-define(wxLocale_GetName, 3565).
+-define(wxLocale_GetString_2, 3566).
+-define(wxLocale_GetString_4, 3567).
+-define(wxLocale_GetHeaderValue, 3568).
+-define(wxLocale_GetSysName, 3569).
+-define(wxLocale_GetSystemEncoding, 3570).
+-define(wxLocale_GetSystemEncodingName, 3571).
+-define(wxLocale_GetSystemLanguage, 3572).
+-define(wxLocale_IsLoaded, 3573).
+-define(wxLocale_IsOk, 3574).
+-define(wxActivateEvent_GetActive, 3575).
+-define(wxPopupWindow_new_2, 3577).
+-define(wxPopupWindow_new_0, 3578).
+-define(wxPopupWindow_destruct, 3580).
+-define(wxPopupWindow_Create, 3581).
+-define(wxPopupWindow_Position, 3582).
+-define(wxPopupTransientWindow_new_0, 3583).
+-define(wxPopupTransientWindow_new_2, 3584).
+-define(wxPopupTransientWindow_destruct, 3585).
+-define(wxPopupTransientWindow_Popup, 3586).
+-define(wxPopupTransientWindow_Dismiss, 3587).
+-define(wxOverlay_new, 3588).
+-define(wxOverlay_destruct, 3589).
+-define(wxOverlay_Reset, 3590).
+-define(wxDCOverlay_new_6, 3591).
+-define(wxDCOverlay_new_2, 3592).
+-define(wxDCOverlay_destruct, 3593).
+-define(wxDCOverlay_Clear, 3594).
diff --git a/lib/wx/src/wx.erl b/lib/wx/src/wx.erl
index a1a9344316..6498b58cda 100644
--- a/lib/wx/src/wx.erl
+++ b/lib/wx/src/wx.erl
@@ -66,7 +66,7 @@
get_env/0,set_env/1, debug/1,
batch/1,foreach/2,map/2,foldl/3,foldr/3,
getObjectType/1, typeCast/2,
- null/0, is_null/1]).
+ null/0, is_null/1, equal/2]).
-export([create_memory/1, get_memory_bin/1,
retain_memory/1, release_memory/1]).
@@ -153,6 +153,10 @@ null() ->
-spec is_null(wx_object()) -> boolean().
is_null(#wx_ref{ref=NULL}) -> NULL =:= 0.
+%% @doc Returns true if both arguments references the same object, false otherwise
+-spec equal(wx_object(), wx_object()) -> boolean().
+equal(#wx_ref{ref=Ref1}, #wx_ref{ref=Ref2}) -> Ref1 =:= Ref2.
+
%% @doc Returns the object type
-spec getObjectType(wx_object()) -> atom().
getObjectType(#wx_ref{type=Type}) ->
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index a2acbb0a63..c22a083eb9 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -107,7 +107,8 @@
call/2, call/3,
cast/2,
reply/2,
- get_pid/1
+ get_pid/1,
+ set_pid/2
]).
%% -export([behaviour_info/1]).
@@ -306,6 +307,11 @@ cast(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
get_pid(#wx_ref{state=Pid}) when is_pid(Pid) ->
Pid.
+%% @spec (Ref::wxObject(), pid()) -> wxObject()
+%% @doc Sets the controlling process of the object handle.
+set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
+ R#wx_ref{state=Pid}.
+
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
diff --git a/lib/wx/test/wx_app_SUITE.erl b/lib/wx/test/wx_app_SUITE.erl
index 0b885a78b8..3fd5bf689d 100644
--- a/lib/wx/test/wx_app_SUITE.erl
+++ b/lib/wx/test/wx_app_SUITE.erl
@@ -49,7 +49,7 @@ end_per_testcase(Func,Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,5}}].
all() ->
[fields, modules, exportall, app_depend, undef_funcs, appup].
@@ -221,12 +221,10 @@ check_apps([App|Apps]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-undef_funcs(suite) ->
- [];
-undef_funcs(doc) ->
- [];
+undef_funcs() ->
+ [{timetrap,{minutes,10}}].
+
undef_funcs(Config) when is_list(Config) ->
- catch test_server:timetrap(timer:minutes(10)),
App = wx,
AppFile = key1search(app_file, Config),
Mods = key1search(modules, AppFile),
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index 5dffdea6be..f89f25274a 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -45,7 +45,7 @@ end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,2}}].
all() ->
[silent_start, create_window, several_apps, wx_api, wx_misc,
@@ -344,13 +344,13 @@ data_types(_Config) ->
ImgRGB = ?mt(wxImage, wxImage:new(128, 64, Colors)),
?m(true, wxImage:ok(ImgRGB)),
?m(false, wxImage:hasAlpha(ImgRGB)),
- ?m(Colors, wxImage:getData(ImgRGB)),
+ ?m(ok, case wxImage:getData(ImgRGB) of Colors -> ok; Other -> Other end),
ImgRGBA = ?mt(wxImage, wxImage:new(128, 64, Colors, Alpha)),
?m(true, wxImage:ok(ImgRGBA)),
?m(true, wxImage:hasAlpha(ImgRGBA)),
- ?m(Colors, wxImage:getData(ImgRGBA)),
- ?m(Alpha, wxImage:getAlpha(ImgRGBA)),
+ ?m(ok, case wxImage:getData(ImgRGBA) of Colors -> ok; Other -> Other end),
+ ?m(ok, case wxImage:getAlpha(ImgRGBA) of Alpha -> ok; Other -> Other end),
wxClientDC:destroy(CDC),
%%wx_test_lib:wx_destroy(Frame,Config).
@@ -361,7 +361,8 @@ wx_object(Config) ->
wx:new(),
Me = self(),
Init = fun() ->
- Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Test wx_object", [{size, {500, 400}}]),
+ Frame0 = wxFrame:new(wx:null(), ?wxID_ANY, "Test wx_object", [{size, {500, 400}}]),
+ Frame = wx_object:set_pid(Frame0, self()),
Sz = wxBoxSizer:new(?wxHORIZONTAL),
Panel = wxPanel:new(Frame),
wxSizer:add(Sz, Panel, [{flag, ?wxEXPAND}, {proportion, 1}]),
@@ -371,6 +372,7 @@ wx_object(Config) ->
{Frame, {Frame, Panel}}
end,
Frame = ?mt(wxFrame, wx_obj_test:start([{init, Init}])),
+
timer:sleep(500),
?m(ok, check_events(flush())),
@@ -378,6 +380,11 @@ wx_object(Config) ->
?m({call, foobar, {Me, _}}, wx_object:call(Frame, foobar)),
?m(ok, wx_object:cast(Frame, foobar2)),
?m([{cast, foobar2}|_], flush()),
+
+ ?m(Frame, wx_obj_test:who_are_you(Frame)),
+ {call, {Frame,Panel}, _} = wx_object:call(Frame, fun(US) -> US end),
+ ?m(false, wxWindow:getParent(Panel) =:= Frame),
+ ?m(true, wx:equal(wxWindow:getParent(Panel),Frame)),
FramePid = wx_object:get_pid(Frame),
io:format("wx_object pid ~p~n",[FramePid]),
FramePid ! foo3,
diff --git a/lib/wx/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl
index e23fd901f5..6944a78360 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -46,12 +46,12 @@ end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,2}}].
all() ->
[calendarCtrl, treeCtrl, notebook, staticBoxSizer,
clipboard, helpFrame, htmlWindow, listCtrlSort, listCtrlVirtual,
- radioBox, systemSettings, taskBarIcon, toolbar, popup].
+ radioBox, systemSettings, taskBarIcon, toolbar, popup, modal].
groups() ->
[].
@@ -621,3 +621,70 @@ lang_env() ->
format_env({match, List}) ->
[io:format(" ~ts~n",[L]) || L <- List];
format_env(nomatch) -> ok.
+
+%% Add a testcase that tests that we can recurse in showModal
+%% because it hangs in observer if object are not destroyed correctly
+%% when popping the stack
+
+modal(Config) ->
+ Wx = wx:new(),
+ case {?wxMAJOR_VERSION, ?wxMINOR_VERSION, ?wxRELEASE_NUMBER} of
+ {2, Min, Rel} when Min < 8 orelse (Min =:= 8 andalso Rel < 11) ->
+ {skip, "old wxWidgets version"};
+ _ ->
+ Frame = wxFrame:new(Wx, -1, "Test Modal windows"),
+ wxFrame:show(Frame),
+ Env = wx:get_env(),
+ Tester = self(),
+ ets:new(test_state, [named_table, public]),
+ Upd = wxUpdateUIEvent:getUpdateInterval(),
+ wxUpdateUIEvent:setUpdateInterval(500),
+ _Pid = spawn(fun() ->
+ wx:set_env(Env),
+ modal_dialog(Frame, 1, Tester)
+ end),
+ %% need to sleep so we know that the window is stuck in
+ %% the ShowModal event loop and not in an earlier event loop
+ %% wx2.8 invokes the event loop from more calls than wx-3
+ receive {dialog, M1, 1} -> timer:sleep(1200), ets:insert(test_state, {M1, ready}) end,
+ receive {dialog, M2, 2} -> timer:sleep(1200), ets:insert(test_state, {M2, ready}) end,
+
+ receive done -> ok end,
+ receive {dialog_done, M2, 2} -> M2 end,
+ receive {dialog_done, M1, 1} -> M1 end,
+
+ wxUpdateUIEvent:setUpdateInterval(Upd),
+ wx_test_lib:wx_destroy(Frame,Config)
+ end.
+
+modal_dialog(Parent, Level, Tester) when Level < 3 ->
+ M1 = wxTextEntryDialog:new(Parent, "Dialog " ++ integer_to_list(Level)),
+ io:format("Creating dialog ~p ~p~n",[Level, M1]),
+ wxDialog:connect(M1, show, [{callback, fun(#wx{event=Ev},_) ->
+ case Ev of
+ #wxShow{show=true} ->
+ Tester ! {dialog, M1, Level};
+ _ -> ignore
+ end
+ end}]),
+ DoOnce = fun(_,_) ->
+ case ets:take(test_state, M1) of
+ [] -> ignore;
+ [_] -> modal_dialog(M1, Level+1, Tester)
+ end
+ end,
+ wxDialog:connect(M1, update_ui, [{callback, DoOnce}]),
+ ?wxID_OK = wxDialog:showModal(M1),
+ wxDialog:destroy(M1),
+ case Level > 1 of
+ true ->
+ io:format("~p: End dialog ~p ~p~n",[?LINE, Level-1, Parent]),
+ wxDialog:endModal(Parent, ?wxID_OK);
+ false -> ok
+ end,
+ Tester ! {dialog_done, M1, Level},
+ ok;
+modal_dialog(Parent, Level, Tester) ->
+ io:format("~p: End dialog ~p ~p~n",[?LINE, Level-1, Parent]),
+ wxDialog:endModal(Parent, ?wxID_OK),
+ Tester ! done.
diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl
index 62fcf44033..6512cedaf2 100644
--- a/lib/wx/test/wx_event_SUITE.erl
+++ b/lib/wx/test/wx_event_SUITE.erl
@@ -44,7 +44,7 @@ end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,2}}].
all() ->
[connect, disconnect, disconnect_cb, connect_msg_20, connect_cb_20,
diff --git a/lib/wx/test/wx_obj_test.erl b/lib/wx/test/wx_obj_test.erl
index cf99728c1a..23142e28b2 100644
--- a/lib/wx/test/wx_obj_test.erl
+++ b/lib/wx/test/wx_obj_test.erl
@@ -19,13 +19,13 @@
-module(wx_obj_test).
-include_lib("wx/include/wx.hrl").
--export([start/1, stop/1]).
+-export([start/1, stop/1, who_are_you/1]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_sync_event/3, handle_event/2, handle_cast/2]).
--record(state, {parent, opts, user_state}).
+-record(state, {parent, me, opts, user_state}).
start(Opts) ->
wx_object:start_link(?MODULE, [{parent, self()}| Opts], []).
@@ -33,12 +33,15 @@ start(Opts) ->
stop(Object) ->
wx_object:stop(Object).
+who_are_you(Object) ->
+ wx_object:call(Object, who_are_you).
+
init(Opts) ->
Parent = proplists:get_value(parent, Opts),
put(parent_pid, Parent),
Init = proplists:get_value(init, Opts),
{Obj, UserState} = Init(),
- {Obj, #state{parent=Parent, opts=Opts, user_state=UserState}}.
+ {Obj, #state{me=Obj, parent=Parent, opts=Opts, user_state=UserState}}.
handle_sync_event(Event = #wx{obj=Panel, event=#wxPaint{}},
WxEvent, #state{parent=Parent, user_state=US, opts=Opts}) ->
@@ -59,6 +62,8 @@ handle_event(Event, State = #state{parent=Parent}) ->
Parent ! {event, Event},
{noreply, State}.
+handle_call(who_are_you, _From, State = #state{me=Me}) ->
+ {reply, Me, State};
handle_call(What, From, State = #state{user_state=US}) when is_function(What) ->
Result = What(US),
{reply, {call, Result, From}, State};
diff --git a/lib/wx/test/wx_opengl_SUITE.erl b/lib/wx/test/wx_opengl_SUITE.erl
index 5162078dbf..643a0df6a3 100644
--- a/lib/wx/test/wx_opengl_SUITE.erl
+++ b/lib/wx/test/wx_opengl_SUITE.erl
@@ -52,7 +52,7 @@ end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,2}}].
all() ->
[canvas, glu_tesselation].
diff --git a/lib/wx/test/wx_xtra_SUITE.erl b/lib/wx/test/wx_xtra_SUITE.erl
index 7aba17ee74..c6268a7f46 100644
--- a/lib/wx/test/wx_xtra_SUITE.erl
+++ b/lib/wx/test/wx_xtra_SUITE.erl
@@ -45,7 +45,7 @@ end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,2}}].
all() ->
[destroy_app, multiple_add_in_sizer, app_dies,
diff --git a/lib/wx/test/wxt.erl b/lib/wx/test/wxt.erl
index fc828e47e8..265cd5c981 100644
--- a/lib/wx/test/wxt.erl
+++ b/lib/wx/test/wxt.erl
@@ -16,13 +16,9 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-%%%-------------------------------------------------------------------
-%%% File : wxt.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Shortcuts for starting test with wx internal test_server
-%%%
-%%% Created : 4 Nov 2008 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
+%%
+%% Description : Shortcuts for running tests with wx internal test_server
+%%-------------------------------------------------------------------
-module(wxt).
-compile(export_all).
@@ -40,7 +36,7 @@ t(Mod, TC) when is_atom(Mod), is_atom(TC) ->
t({Mod,TC}, []);
t(all, Config) when is_list(Config) ->
Fs = filelib:wildcard("wx_*_SUITE.erl"),
- t([list_to_atom(filename:rootname(File)) || File <- Fs], Config);
+ t([list_to_atom(filename:rootname(File)) || File <- Fs, File =/= "wx_app_SUITE.erl"], Config);
t(Test,Config) when is_list(Config) ->
Tests = resolve(Test),
write_test_case(Test),