aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin32008 -> 32080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin13336 -> 12796 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin53716 -> 53896 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin24872 -> 24808 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin10592 -> 10672 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17568 -> 17548 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin8424 -> 8132 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin28200 -> 28828 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin89676 -> 90264 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin82296 -> 82864 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin26796 -> 27352 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin12988 -> 13588 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29768 -> 29912 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/maps.beambin2404 -> 2892 bytes
-rw-r--r--erts/configure.in48
-rw-r--r--erts/doc/src/absform.xml13
-rw-r--r--erts/doc/src/erl.xml52
-rw-r--r--erts/doc/src/erl_dist_protocol.xml4
-rw-r--r--erts/doc/src/erl_tracer.xml358
-rw-r--r--erts/doc/src/erlang.xml535
-rw-r--r--erts/emulator/beam/atom.names6
-rw-r--r--erts/emulator/beam/beam_bif_load.c75
-rw-r--r--erts/emulator/beam/beam_bp.c11
-rw-r--r--erts/emulator/beam/beam_bp.h20
-rw-r--r--erts/emulator/beam/beam_emu.c135
-rw-r--r--erts/emulator/beam/beam_load.c101
-rw-r--r--erts/emulator/beam/bif.c106
-rw-r--r--erts/emulator/beam/bif.h1
-rw-r--r--erts/emulator/beam/bif.tab2
-rw-r--r--erts/emulator/beam/break.c6
-rw-r--r--erts/emulator/beam/dist.c12
-rw-r--r--erts/emulator/beam/erl_alloc.c113
-rw-r--r--erts/emulator/beam/erl_async.c4
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c2
-rw-r--r--erts/emulator/beam/erl_bif_info.c58
-rw-r--r--erts/emulator/beam/erl_bif_trace.c139
-rw-r--r--erts/emulator/beam/erl_db.c3
-rw-r--r--erts/emulator/beam/erl_db_util.c411
-rw-r--r--erts/emulator/beam/erl_db_util.h8
-rw-r--r--erts/emulator/beam/erl_gc.c325
-rw-r--r--erts/emulator/beam/erl_gc.h6
-rw-r--r--erts/emulator/beam/erl_hl_timer.c8
-rw-r--r--erts/emulator/beam/erl_init.c110
-rw-r--r--erts/emulator/beam/erl_map.c75
-rw-r--r--erts/emulator/beam/erl_map.h1
-rw-r--r--erts/emulator/beam/erl_message.c103
-rw-r--r--erts/emulator/beam/erl_message.h8
-rw-r--r--erts/emulator/beam/erl_msacc.c2
-rw-r--r--erts/emulator/beam/erl_nif.c96
-rw-r--r--erts/emulator/beam/erl_process.c83
-rw-r--r--erts/emulator/beam/erl_process.h42
-rw-r--r--erts/emulator/beam/erl_time_sup.c2
-rw-r--r--erts/emulator/beam/erl_trace.c522
-rw-r--r--erts/emulator/beam/erl_trace.h13
-rw-r--r--erts/emulator/beam/erl_vm.h3
-rw-r--r--erts/emulator/beam/erlang_lttng.h8
-rw-r--r--erts/emulator/beam/external.c18
-rw-r--r--erts/emulator/beam/global.h24
-rw-r--r--erts/emulator/beam/io.c76
-rw-r--r--erts/emulator/beam/sys.h3
-rw-r--r--erts/emulator/beam/utils.c37
-rw-r--r--erts/emulator/hipe/hipe_bif0.c24
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c107
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c2
-rw-r--r--erts/emulator/nifs/common/erl_tracer_nif.c7
-rw-r--r--erts/emulator/sys/common/erl_mmap.c28
-rw-r--r--erts/emulator/sys/common/erl_mmap.h104
-rw-r--r--erts/emulator/sys/common/erl_mseg.c127
-rw-r--r--erts/emulator/sys/common/erl_mseg.h2
-rw-r--r--erts/emulator/sys/unix/sys_float.c84
-rw-r--r--erts/emulator/sys/win32/sys_float.c3
-rw-r--r--erts/emulator/test/alloc_SUITE.erl4
-rw-r--r--erts/emulator/test/lttng_SUITE.erl10
-rw-r--r--erts/emulator/test/map_SUITE.erl74
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl11
-rw-r--r--erts/emulator/test/message_queue_data_SUITE.erl32
-rw-r--r--erts/emulator/test/port_trace_SUITE.erl34
-rw-r--r--erts/emulator/test/port_trace_SUITE_data/echo_drv.c4
-rw-r--r--erts/emulator/test/process_SUITE.erl186
-rw-r--r--erts/emulator/test/save_calls_SUITE.erl2
-rw-r--r--erts/emulator/test/sensitive_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_SUITE.erl328
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl8
-rw-r--r--erts/emulator/test/tracer_SUITE.erl8
-rw-r--r--erts/etc/common/erlexec.c26
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin55812 -> 55780 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin1924 -> 2112 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin104196 -> 104620 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin8724 -> 8696 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin10560 -> 10536 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin46288 -> 49924 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1468 -> 1444 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1340 -> 1312 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin44828 -> 44764 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin72632 -> 72544 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23184 -> 23152 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin14176 -> 14136 bytes
-rw-r--r--erts/preloaded/src/erl_tracer.erl29
-rw-r--r--erts/preloaded/src/erlang.erl41
-rw-r--r--erts/preloaded/src/init.erl76
-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/compiler/src/cerl.erl7
-rw-r--r--lib/compiler/src/core_pp.erl54
-rw-r--r--lib/compiler/src/v3_core.erl23
-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.erl46
-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/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/doc/src/diameter.xml20
-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/diameter.appup.src10
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl251
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl8
-rw-r--r--lib/diameter/vsn.mk4
-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/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/notes.xml35
-rw-r--r--lib/inets/src/inets_app/inets.appup.src2
-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.erl20
-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/odbc/configure.in12
-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/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.erl250
-rw-r--r--lib/runtime_tools/test/dyntrace_lttng_SUITE.erl377
-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.erl44
-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.erl23
-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.erl74
-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.erl9
-rw-r--r--lib/ssl/src/ssl_cipher.erl16
-rw-r--r--lib/ssl/src/ssl_connection.erl946
-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.erl4
-rw-r--r--lib/ssl/src/tls_connection.erl608
-rw-r--r--lib/ssl/src/tls_handshake.erl8
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl184
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl133
-rw-r--r--lib/ssl/test/ssl_test_lib.erl9
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/epp.xml1
-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.xml7
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl4
-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/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/proc_lib.erl9
-rw-r--r--lib/stdlib/src/rand.erl10
-rw-r--r--lib/stdlib/src/supervisor.erl4
-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/lists_SUITE.erl16
-rw-r--r--lib/stdlib/test/maps_SUITE.erl56
-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/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/wxapi.conf6
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp28
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h3834
-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/test/wx_class_SUITE.erl7
-rwxr-xr-xotp_build4
-rw-r--r--otp_versions.table2
-rw-r--r--system/doc/reference_manual/code_loading.xml55
-rw-r--r--system/doc/reference_manual/macros.xml48
-rw-r--r--system/doc/reference_manual/typespec.xml32
431 files changed, 34268 insertions, 15052 deletions
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index 1fea28a4dc..82bd89c17a 100644
--- a/bootstrap/lib/compiler/ebin/cerl.beam
+++ b/bootstrap/lib/compiler/ebin/cerl.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index 8f97709248..6047596cb6 100644
--- a/bootstrap/lib/compiler/ebin/core_pp.beam
+++ b/bootstrap/lib/compiler/ebin/core_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index 9b2450089a..3b3b8f4208 100644
--- a/bootstrap/lib/compiler/ebin/v3_core.beam
+++ b/bootstrap/lib/compiler/ebin/v3_core.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index aa1ba78550..6f99487ca7 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index 369871f398..eb09a06785 100644
--- a/bootstrap/lib/kernel/ebin/dist_util.beam
+++ b/bootstrap/lib/kernel/ebin/dist_util.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam
index 1269f7e321..03202f68eb 100644
--- a/bootstrap/lib/kernel/ebin/global_group.beam
+++ b/bootstrap/lib/kernel/ebin/global_group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index 3b77ed641a..684d95afa0 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 5c98ffdafc..54909eeaa3 100644
--- a/bootstrap/lib/stdlib/ebin/epp.beam
+++ b/bootstrap/lib/stdlib/ebin/epp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 22d3607866..aeea1e2fcd 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam
index efe68730d5..9b04739d58 100644
--- a/bootstrap/lib/stdlib/ebin/erl_parse.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_parse.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam
index 4cc4884c92..9841362f9d 100644
--- a/bootstrap/lib/stdlib/ebin/erl_pp.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_statem.beam b/bootstrap/lib/stdlib/ebin/gen_statem.beam
index 08cb19ca6f..5a3fd2a1a5 100644
--- a/bootstrap/lib/stdlib/ebin/gen_statem.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_statem.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index 91297311b2..422a9bd214 100644
--- a/bootstrap/lib/stdlib/ebin/lists.beam
+++ b/bootstrap/lib/stdlib/ebin/lists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam
index 7533b56554..5f041ab10f 100644
--- a/bootstrap/lib/stdlib/ebin/maps.beam
+++ b/bootstrap/lib/stdlib/ebin/maps.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index 11c428ced7..6765f47a88 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -62,9 +62,6 @@ if test x"${ERL_TOP}/erts" != x"$srcdir"; then
fi
erl_top=${ERL_TOP}
-# Remove old configuration information
-/bin/rm -f "$ERL_TOP/erts/CONF_INFO"
-
# echo XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# echo X
# echo "X srcdir = $srcdir"
@@ -729,8 +726,27 @@ esac
AC_SUBST(LIBCARBON)
-dnl Check if we should/can build a sharing-preserving emulator
+_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
+
+AC_PATH_PROG(RM, rm, false, $_search_path)
+if test "$ac_cv_path_RM" = false; then
+ AC_MSG_ERROR([No 'rm' command found])
+fi
+
+AC_PATH_PROG(MKDIR, mkdir, false, $_search_path)
+if test "$ac_cv_path_MKDIR" = false; then
+ AC_MSG_ERROR([No 'mkdir' command found])
+fi
+
+_search_path=
+
+# Remove old configuration information.
+# Next line should be placed after AC_PATH_PROG(RM, ...), but before
+# first output to CONN_INFO. So this is just the right place.
+$RM -f "$ERL_TOP/erts/CONF_INFO"
+
+dnl Check if we should/can build a sharing-preserving emulator
AC_MSG_CHECKING(if we are building a sharing-preserving emulator)
if test "$enable_sharing_preserving" = "yes"; then
AC_DEFINE(SHCOPY, [1],
@@ -760,27 +776,13 @@ if test "$ac_cv_prog_AR" = false; then
AC_MSG_ERROR([No 'ar' command found in PATH])
fi
-_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
-
-AC_PATH_PROG(RM, rm, false, $_search_path)
-if test "$ac_cv_path_RM" = false; then
- AC_MSG_ERROR([No 'rm' command found])
-fi
-
-AC_PATH_PROG(MKDIR, mkdir, false, $_search_path)
-if test "$ac_cv_path_MKDIR" = false; then
- AC_MSG_ERROR([No 'mkdir' command found])
-fi
-
-_search_path=
-
#
# Get programs needed for building the documentation
#
## Delete previous failed configure results
if test -f doc/CONF_INFO; then
- rm doc/CONF_INFO
+ $RM doc/CONF_INFO
fi
AC_CHECK_PROGS(XSLTPROC, xsltproc)
@@ -3823,7 +3825,7 @@ if test "$enable_dtrace_test" = "yes" ; then
[$RM -f $DTRACE_2STEP_TEST
dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d conftest.$OBJEXT 2>&AS_MESSAGE_LOG_FD
if test -f $DTRACE_2STEP_TEST; then
- rm $DTRACE_2STEP_TEST
+ $RM $DTRACE_2STEP_TEST
DTRACE_ENABLED_2STEP=yes
fi],
[])
@@ -4030,7 +4032,7 @@ ssl_done=yes # Default only one run
# Remove all SKIP files from previous runs
for a in ssl crypto ssh; do
- /bin/rm -f $ERL_TOP/lib/$a/SKIP
+ $RM -f $ERL_TOP/lib/$a/SKIP
done
SSL_DYNAMIC_ONLY=$enable_dynamic_ssl
@@ -4627,7 +4629,7 @@ need_java="jinterface ic/java_src"
# Remove all SKIP files from previous runs
for a in $need_java ; do
- /bin/rm -f $ERL_TOP/lib/$a/SKIP
+ $RM -f $ERL_TOP/lib/$a/SKIP
done
if test "X$with_javac" = "Xno"; then
@@ -4678,7 +4680,7 @@ dnl this deliberately does not believe that 'gcc' is a C++ compiler
AC_CHECK_TOOLS(CXX, [$CCC c++ g++ CC cxx cc++ cl], false)
# Remove SKIP file from previous run
-/bin/rm -f $ERL_TOP/lib/orber/SKIP
+$RM -f $ERL_TOP/lib/orber/SKIP
if test "$CXX" = false; then
echo "No C++ compiler found" > $ERL_TOP/lib/orber/SKIP
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml
index 13756ddfdc..bfabb7f042 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -68,22 +68,12 @@
<item>If D is a module declaration consisting of the forms
<c>F_1</c>, ..., <c>F_k</c>, then
Rep(D) = <c>[Rep(F_1), ..., Rep(F_k)]</c>.</item>
- <item>If F is an attribute <c>-behavior(Behavior)</c>, then
- Rep(F) = <c>{attribute,LINE,behavior,Behavior}</c>.</item>
- <item>If F is an attribute <c>-behaviour(Behaviour)</c>, then
- Rep(F) = <c>{attribute,LINE,behaviour,Behaviour}</c>.</item>
- <item>If F is an attribute <c>-compile(Options)</c>, then
- Rep(F) = <c>{attribute,LINE,compile,Options}</c>.</item>
<item>If F is an attribute <c>-export([Fun_1/A_1, ..., Fun_k/A_k])</c>, then
Rep(F) = <c>{attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}</c>.</item>
- <item>If F is an attribute <c>-export_type([Type_1/A_1, ..., Type_k/A_k])</c>, then
- Rep(F) = <c>{attribute,LINE,export_type,[{Type_1,A_1}, ..., {Type_k,A_k}]}</c>.</item>
<item>If F is an attribute <c>-import(Mod,[Fun_1/A_1, ..., Fun_k/A_k])</c>, then
Rep(F) = <c>{attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}</c>.</item>
<item>If F is an attribute <c>-module(Mod)</c>, then
Rep(F) = <c>{attribute,LINE,module,Mod}</c>.</item>
- <item>If F is an attribute <c>-optional_callbacks([Fun_1/A_1, ..., Fun_k/A_k])</c>, then
- Rep(F) = <c>{attribute,LINE,optional_callbacks,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}</c>.</item>
<item>If F is an attribute <c>-file(File,Line)</c>, then
Rep(F) = <c>{attribute,LINE,file,{File,Line}}</c>.</item>
<item>If F is a function declaration
@@ -636,6 +626,9 @@
<item>If A is an association type <c>K => V</c>, where
<c>K</c> and <c>V</c> are types, then Rep(A) =
<c>{type,LINE,map_field_assoc,[Rep(K),Rep(V)]}</c>.</item>
+ <item>If A is an association type <c>K := V</c>, where
+ <c>K</c> and <c>V</c> are types, then Rep(A) =
+ <c>{type,LINE,map_field_exact,[Rep(K),Rep(V)]}</c>.</item>
</list>
</section>
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index e13470c83c..1bbde7f1e0 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -627,11 +627,48 @@
<p>Sets the default binary virtual heap size of processes to the size
<c><![CDATA[Size]]></c>.</p>
</item>
+ <marker id="+hmax"/>
+ <tag><c><![CDATA[+hmax Size]]></c></tag>
+ <item>
+ <p>Sets the default maximum heap size of processes to the size
+ <c><![CDATA[Size]]></c>. If <c>+hmax</c> is not given, the default is <c>0</c>
+ which means that no maximum heap size is used.
+ For more information, see the documentation of
+ <seealso marker="erlang#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
+ <marker id="+hmaxel"/>
+ <tag><c><![CDATA[+hmaxel true|false]]></c></tag>
+ <item>
+ <p>Sets whether to send an error logger message for processes that reach
+ the maximum heap size or not. If <c>+hmaxel</c> is not given, the default is <c>true</c>.
+ For more information, see the documentation of
+ <seealso marker="erlang#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
+ <marker id="+hmaxk"/>
+ <tag><c><![CDATA[+hmaxk true|false]]></c></tag>
+ <item>
+ <p>Sets whether to kill processes that reach the maximum heap size or not. If
+ <c>+hmaxk</c> is not given, the default is <c>true</c>. For more information,
+ see the documentation of
+ <seealso marker="erlang#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ </item>
<tag><c><![CDATA[+hpds Size]]></c></tag>
<item>
<p>Sets the initial process dictionary size of processes to the size
<c><![CDATA[Size]]></c>.</p>
</item>
+ <tag><marker id="+hmqd"><c>+hmqd off_heap|on_heap|mixed</c></marker></tag>
+ <item><p>
+ Sets the default value for the process flag
+ <c>message_queue_data</c>. If <c>+hmqd</c> is not
+ passed, <c>mixed</c> will be the default. For more information,
+ see the documentation of
+ <seealso marker="erlang#process_flag_message_queue_data"><c>process_flag(message_queue_data,
+ MQD)</c></seealso>.
+ </p></item>
<tag><c><![CDATA[+K true | false]]></c></tag>
<item>
<p>Enables or disables the kernel poll functionality if
@@ -1361,21 +1398,6 @@
<seealso marker="kernel:error_logger#warning_map/0">error_logger(3)</seealso>
for further information.</p>
</item>
- <tag><c><![CDATA[+xFlag Value]]></c></tag>
- <item>
- <p>Default process flag settings.</p>
- <taglist>
- <tag><marker id="+xmqd"><c>+xmqd off_heap|on_heap|mixed</c></marker></tag>
- <item><p>
- Sets the default value for the process flag
- <c>message_queue_data</c>. If <c>+xmqd</c> is not
- passed, <c>mixed</c> will be the default. For more information,
- see the documentation of
- <seealso marker="erlang#process_flag_message_queue_data"><c>process_flag(message_queue_data,
- MQD)</c></seealso>.
- </p></item>
- </taglist>
- </item>
<tag><c><![CDATA[+zFlag Value]]></c></tag>
<item>
<p>Miscellaneous flags.</p>
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index b435d5c9b4..f9fa981d9a 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -364,14 +364,14 @@ If Result > 0, the packet only consists of [119, Result].
NodeInfo is, as expressed in Erlang:
</p>
<code>
- io:format("active name ~ts at port ~p, fd = ~p ~n",
+ io:format("active name ~ts at port ~p, fd = ~p~n",
[NodeName, Port, Fd]).
</code>
<p>
or
</p>
<code>
- io:format("old/unused name ~ts at port ~p, fd = ~p~n",
+ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
[NodeName, Port, Fd]).
</code>
diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml
index 1e8e78b25f..2075b962d8 100644
--- a/erts/doc/src/erl_tracer.xml
+++ b/erts/doc/src/erl_tracer.xml
@@ -54,6 +54,14 @@
</description>
<datatypes>
+ <datatype> <name name="trace_tag_send" /> </datatype>
+ <datatype> <name name="trace_tag_receive" /> </datatype>
+ <datatype> <name name="trace_tag_call" /> </datatype>
+ <datatype> <name name="trace_tag_procs" /> </datatype>
+ <datatype> <name name="trace_tag_ports" /> </datatype>
+ <datatype> <name name="trace_tag_running_procs" /> </datatype>
+ <datatype> <name name="trace_tag_running_ports" /> </datatype>
+ <datatype> <name name="trace_tag_gc" /> </datatype>
<datatype>
<name name="trace_tag" />
<desc>
@@ -105,6 +113,29 @@
<title>CALLBACK FUNCTIONS</title>
<p>The following functions
should be exported from a <c>erl_tracer</c> callback module.</p>
+ <taglist>
+ <tag><seealso marker="#enabled"><c>Module:enabled/3</c></seealso></tag>
+ <item>Mandatory</item>
+ <tag><seealso marker="#trace"><c>Module:trace/6</c></seealso></tag>
+ <item>Mandatory</item>
+ <tag><seealso marker="#enabled_procs"><c>Module:enabled_procs/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#trace_procs"><c>Module:trace_procs/6</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#enabled_ports"><c>Module:enabled_ports/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#trace_ports"><c>Module:trace_ports/6</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#enabled_running_ports"><c>Module:enabled_running_ports/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#trace_running_ports"><c>Module:trace_running_ports/6</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#enabled_running_procs"><c>Module:enabled_running_procs/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#trace_running_procs"><c>Module:trace_running_procs/6</c></seealso></tag>
+ <item>Optional</item>
+ </taglist>
+
</section>
<marker id="enabled"></marker>
<funcs>
@@ -114,11 +145,11 @@
<type>
<v>TraceTag = <seealso marker="#type-trace_tag">trace_tag()</seealso> | trace_status</v>
<v>TracerState = term()</v>
- <v>Tracee = <seealso marker="#type-trace_tag">tracee()</seealso></v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>Result = trace | discard | remove</v>
</type>
<desc>
- <p>This callback will be called whenever a trace point is triggered. It
+ <p>This callback will be called whenever a tracepoint is triggered. It
allows the tracer to decide whether a trace should be generated or not.
This check is made as early as possible in order to limit the amount of
overhead associated with tracing. If <c>trace</c> is returned the
@@ -132,7 +163,7 @@
to check if the tracer should still be active. It is called in multiple
scenarios, but most significantly it is used when tracing is started
using this tracer.</p>
- <p>This function may be called multiple times per trace point, so it
+ <p>This function may be called multiple times per tracepoint, so it
is important that it is both fast and side effect free.</p>
</desc>
</func>
@@ -143,17 +174,17 @@
<type>
<v>TraceTag = <seealso marker="#type-trace_tag">trace_tag()</seealso></v>
<v>TracerState = term()</v>
- <v>Tracee = <seealso marker="#type-trace_tag">tracee()</seealso></v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
<v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
<desc>
- <p>This callback will be called when a trace point is triggered and
+ <p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#enabled">Module:enabled/3</seealso>
callback returned <c>trace</c>. In it any side effects needed by
- the tracer should be done. The trace point payload is located in
+ the tracer should be done. The tracepoint payload is located in
the <c>FirstTraceTerm</c> and <c>SecondTraceTerm</c>. The content
of the TraceTerms depends on which <c>TraceTag</c> has been triggered.
The <c>FirstTraceTerm</c> and <c>SecondTraceTerm</c> correspond to the
@@ -181,6 +212,319 @@
see the <seealso marker="kernel:seq_trace">seq_trace</seealso> manual.</p>
</desc>
</func>
+
+ <marker id="enabled_procs"></marker>
+ <func>
+ <name>Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_procs">trace_tag_procs()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>procs</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_procs/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_procs"></marker>
+ <func>
+ <name>Module:trace_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_procs">trace_tag()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_procs">Module:enabled_procs/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_procs/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="enabled_ports"></marker>
+ <func>
+ <name>Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_ports">trace_tag_ports()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>ports</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_ports/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_ports"></marker>
+ <func>
+ <name>Module:trace_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_ports">trace_tag()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_ports">Module:enabled_ports/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_ports/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="enabled_running_procs"></marker>
+ <func>
+ <name>Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_procs">trace_tag_running_procs()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>running_procs | running</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_running_procs/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_running_procs"></marker>
+ <func>
+ <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_procs">trace_tag_running_procs()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_running_procs">Module:enabled_running_procs/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_running_procs/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="enabled_running_ports"></marker>
+ <func>
+ <name>Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_ports">trace_tag_running_ports()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>running_ports</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_running_ports/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_running_ports"></marker>
+ <func>
+ <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_running_ports">trace_tag_running_ports()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_running_ports">Module:enabled_running_ports/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_running_ports/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="enabled_call"></marker>
+ <func>
+ <name>Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_call">trace_tag_call()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>call | return_to</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_call/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_call"></marker>
+ <func>
+ <name>Module:trace_call(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_call">trace_tag_call()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_call">Module:enabled_call/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_call/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="enabled_send"></marker>
+ <func>
+ <name>Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_send">trace_tag_send()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>send</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_send/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_send"></marker>
+ <func>
+ <name>Module:trace_send(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_send">trace_tag_send()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_send">Module:enabled_send/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_send/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="enabled_receive"></marker>
+ <func>
+ <name>Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_receive">trace_tag_receive()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>'receive'</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_receive/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_receive"></marker>
+ <func>
+ <name>Module:trace_receive(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_receive">trace_tag_receive()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_receive">Module:enabled_receive/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_receive/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="enabled_garbage_collection"></marker>
+ <func>
+ <name>Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_gc">trace_tag_gc()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
+ </type>
+ <desc>
+ <p>This callback will be called whenever a tracepoint with trace flag
+ <seealso marker="erlang#trace-3"><c>garbage_collection</c></seealso>
+ is triggered.</p>
+ <p>If <c>enabled_garbage_collection/3</c> is not defined <c>enabled/3</c> will be called instead.</p>
+ </desc>
+ </func>
+
+ <marker id="trace_garbage_collection"></marker>
+ <func>
+ <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event should be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_gc">trace_tag_gc()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>FirstTraceTerm = term()</v>
+ <v>SecondTraceTerm = term() | undefined</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback will be called when a tracepoint is triggered and
+ the <seealso marker="#enabled_garbage_collection">Module:enabled_garbage_collection/3</seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_garbage_collection/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ </desc>
+ </func>
+
</funcs>
<section>
<marker id="example"></marker>
@@ -282,7 +626,7 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifPid to_pid;
if (enif_get_local_pid(env, argv[1], &amp;to_pid))
if (!enif_is_process_alive(env, &amp;to_pid))
- /* tracer is dead so we should remove this trace point */
+ /* tracer is dead so we should remove this tracepoint */
return enif_make_atom(env, "remove");
/* Only generate trace for when tracer != tracee */
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 423ccdf98f..e0723127f2 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4322,6 +4322,7 @@ os_prompt% </pre>
</desc>
</func>
+ <marker id="process_flag_min_heap_size"/>
<func>
<name name="process_flag" arity="2" clause_i="3"/>
<fsummary>Sets process flag <c>min_heap_size</c> for the calling process.</fsummary>
@@ -4340,9 +4341,95 @@ os_prompt% </pre>
<p>Returns the old value of the flag.</p>
</desc>
</func>
- <marker id="process_flag_message_queue_data"/>
+ <marker id="process_flag_max_heap_size"/>
<func>
<name name="process_flag" arity="2" clause_i="5"/>
+ <type name="max_heap_size"/>
+ <fsummary>Sets process flag <c>max_heap_size</c> for the calling process.</fsummary>
+ <desc>
+ <p>
+ This flag sets the maximum heap size for the calling process.
+ If <c><anno>MaxHeapSize</anno></c> is an integer, the system default
+ values for <c>kill</c> and <c>error_logger</c> are used.
+ <taglist>
+ <tag><c>size</c></tag>
+ <item>
+ <p>
+ The maximum size in words of the process. If set to zero, the
+ heap size limit is disabled. Badarg will be thrown if the value is
+ smaller than
+ <seealso marker="#process_flag_min_heap_size"><c>min_heap_size</c></seealso>.
+ The size check is only done when a garbage collection is triggered.
+ </p>
+ <p>
+ <c>size</c> is the entire heap of the process when garbage collection
+ is triggered, this includes all generational heaps, the process stack,
+ any <seealso marker="#process_flag_message_queue_data">
+ messages that are considered to be part of the heap</seealso> and any
+ extra memory that the garbage collector needs during collection.
+ </p>
+ <p>
+ <c>size</c> is the same as can be retrieved using
+ <seealso marker="#process_info_total_heap_size">
+ <c>erlang:process_info(Pid, total_heap_size)</c></seealso>,
+ or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c>
+ and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info">
+ <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>.
+ </p>
+ </item>
+ <tag><c>kill</c></tag>
+ <item>
+ <p>
+ When set to <c>true</c> the runtime system will send an
+ untrappable exit signal with reason <c>kill</c> to the process
+ if the maximum heap size is reached. The garbage collection
+ that triggered the <c>kill</c> will not be completed, instead the
+ process will exit as soon as is possible. When set to <c>false</c>
+ no exit signal will be sent to the process, instead it will
+ continue executing.
+ </p>
+ <p>
+ If <c>kill</c> is not defined in the map
+ the system default will be used. The default system default
+ is <c>true</c>. It can be changed by either the erl
+ <seealso marker="erl#+hmaxk">+hmaxk</seealso> option,
+ or <seealso marker="#system_flag_max_heap_size"><c>
+ erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
+ </p>
+ </item>
+ <tag><c>error_logger</c></tag>
+ <item>
+ <p>
+ When set to <c>true</c> the runtime system will send a
+ message to the current <seealso marker="kernel:error_logger"><c>error_logger</c></seealso>
+ containing details about the process when the maximum
+ heap size is reached. One <c>error_logger</c> report will
+ be sent each time the limit is reached.
+ </p>
+ <p>
+ If <c>error_logger</c> is not defined in the map the system
+ default will be used. The default system default is <c>true</c>.
+ It can be changed by either the erl <seealso marker="erl#+hmaxel">+hmaxel</seealso>
+ option, or <seealso marker="#system_flag_max_heap_size"><c>
+ erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
+ </p>
+ </item>
+ <p>
+ The heap size of a process is quite hard to predict, especially the
+ amount of memory that is used during the garbage collection. When
+ contemplating using this option, it is recommended to first run
+ it in production with <c>kill</c> set to <c>false</c> and inspect
+ the <c>error_logger</c> reports to see what the normal peak sizes
+ of the processes in the system is and then tune the value
+ accordingly.
+ </p>
+ </taglist>
+ </p>
+ </desc>
+ </func>
+ <marker id="process_flag_message_queue_data"/>
+ <func>
+ <name name="process_flag" arity="2" clause_i="6"/>
<fsummary>Set process flag <c>message_queue_data</c> for the calling process</fsummary>
<type name="message_queue_data"/>
<desc>
@@ -4371,7 +4458,7 @@ os_prompt% </pre>
</taglist>
<p>
The default <c>message_queue_data</c> process flag is determined
- by the <seealso marker="erl#+xmqd"><c>+xmqd</c></seealso>
+ by the <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso>
<c>erl</c> command line argument.
</p>
<p>
@@ -4392,7 +4479,7 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="6"/>
+ <name name="process_flag" arity="2" clause_i="7"/>
<fsummary>Sets process flag <c>priority</c> for the calling process.</fsummary>
<type name="priority_level"/>
<desc>
@@ -4466,7 +4553,7 @@ os_prompt% </pre>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="7"/>
+ <name name="process_flag" arity="2" clause_i="8"/>
<fsummary>Sets process flag <c>save_calls</c> for the calling process.</fsummary>
<desc>
<p><c><anno>N</anno></c> must be an integer in the interval 0..10000.
@@ -4497,7 +4584,7 @@ os_prompt% </pre>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="8"/>
+ <name name="process_flag" arity="2" clause_i="9"/>
<fsummary>Sets process flag <c>sensitive</c> for the calling process.</fsummary>
<desc>
<p>Sets or clears flag <c>sensitive</c> for the current process.
@@ -4551,6 +4638,7 @@ os_prompt% </pre>
<type name="process_info_result_item"/>
<type name="priority_level"/>
<type name="stack_item"/>
+ <type name="max_heap_size" />
<type name="message_queue_data" />
<desc>
<p>Returns a list containing <c><anno>InfoTuple</anno></c>s with
@@ -4604,6 +4692,7 @@ os_prompt% </pre>
<type name="process_info_result_item"/>
<type name="stack_item"/>
<type name="priority_level"/>
+ <type name="max_heap_size" />
<type name="message_queue_data" />
<desc>
<p>Returns information about the process identified by
@@ -4696,13 +4785,14 @@ os_prompt% </pre>
The content of <c><anno>GCInfo</anno></c> can be changed without
prior notice.</p>
</item>
+ <marker id="process_info_garbage_collection_info"/>
<tag><c>{garbage_collection_info, <anno>GCInfo</anno>}</c></tag>
<item>
<p><c><anno>GCInfo</anno></c> is a list containing miscellaneous
detailed information about garbage collection for this process.
The content of <c><anno>GCInfo</anno></c> can be changed without
prior notice.
- See <seealso marker="#gc_start">gc_start</seealso> in
+ See <seealso marker="#gc_minor_start">gc_minor_start</seealso> in
<seealso marker="#trace/3">erlang:trace/3</seealso> for details about
what each item means.
</p>
@@ -4875,10 +4965,13 @@ os_prompt% </pre>
total suspend count on <c><anno>Suspendee</anno></c>,
only the parts contributed by <c><anno>Pid</anno></c>.</p>
</item>
+ <marker id="process_info_total_heap_size"/>
<tag><c>{total_heap_size, <anno>Size</anno>}</c></tag>
<item>
<p><c><anno>Size</anno></c> is the total size, in words, of all heap
- fragments of the process. This includes the process stack.</p>
+ fragments of the process. This includes the process stack and
+ any unreceived messages that are considered to be part of the
+ heap. </p>
</item>
<tag><c>{trace, <anno>InternalTraceFlags</anno>}</c></tag>
<item>
@@ -5567,6 +5660,7 @@ true</pre>
<name name="spawn_opt" arity="2"/>
<fsummary>Creates a new process with a fun as entry point.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size" />
<type name="message_queue_data" />
<type name="spawn_opt_option" />
<desc>
@@ -5584,6 +5678,7 @@ true</pre>
<name name="spawn_opt" arity="3"/>
<fsummary>Creates a new process with a fun as entry point on a given node.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size" />
<type name="message_queue_data" />
<type name="spawn_opt_option" />
<desc>
@@ -5600,6 +5695,7 @@ true</pre>
<name name="spawn_opt" arity="4"/>
<fsummary>Creates a new process with a function as entry point.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size" />
<type name="message_queue_data" />
<type name="spawn_opt_option" />
<desc>
@@ -5703,13 +5799,23 @@ true</pre>
fine-tuning an application and to measure the execution
time with various <c><anno>VSize</anno></c> values.</p>
</item>
+ <tag><c>{max_heap_size, <anno>Size</anno>}</c></tag>
+ <item>
+ <p>Sets the <c>max_heap_size</c> process flag. The default
+ <c>max_heap_size</c> is determined by the
+ <seealso marker="erl#+hmax"><c>+hmax</c></seealso> <c>erl</c>
+ command line argument. For more information, see the
+ documentation of
+ <seealso marker="#process_flag_max_heap_size"><c>process_flag(max_heap_size,
+ <anno>Size</anno>)</c></seealso>.</p>
+ </item>
<tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag>
<item>
<p>Sets the state of the <c>message_queue_data</c> process
flag. <c><anno>MQD</anno></c> should be either <c>off_heap</c>,
<c>on_heap</c>, or <c>mixed</c>. The default
<c>message_queue_data</c> process flag is determined by the
- <seealso marker="erl#+xmqd"><c>+xmqd</c></seealso> <c>erl</c>
+ <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso> <c>erl</c>
command line argument. For more information, see the
documentation of
<seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data,
@@ -5723,6 +5829,7 @@ true</pre>
<name name="spawn_opt" arity="5"/>
<fsummary>Creates a new process with a function as entry point on a given node.</fsummary>
<type name="priority_level"/>
+ <type name="max_heap_size" />
<type name="message_queue_data" />
<type name="spawn_opt_option" />
<desc>
@@ -6504,8 +6611,25 @@ ok
</desc>
</func>
+ <marker id="system_flag_max_heap_size"></marker>
<func>
<name name="system_flag" arity="2" clause_i="8"/>
+ <type name="max_heap_size"/>
+ <fsummary>Sets system flag <c>max_heap_size</c></fsummary>
+ <desc>
+ <p>
+ Sets the default maximum heap size settings for processes.
+ The size is given in words. The new <c>max_heap_size</c>
+ effects only processes spawned efter the change has been made.
+ <c>max_heap_size</c> can be set for individual processes using
+ <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
+ <seealso marker="#process_flag_message_queue_data">process_flag/2</seealso>.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_flag" arity="2" clause_i="9"/>
<fsummary>Sets system flag <c>multi_scheduling</c>.</fsummary>
<desc>
<p><marker id="system_flag_multi_scheduling"></marker>
@@ -6555,7 +6679,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="9"/>
+ <name name="system_flag" arity="2" clause_i="10"/>
<fsummary>Sets system flag <c>scheduler_bind_type</c>.</fsummary>
<type name="scheduler_bind_type"/>
<desc>
@@ -6673,7 +6797,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="10"/>
+ <name name="system_flag" arity="2" clause_i="11"/>
<fsummary>Sets system flag <c>scheduler_wall_time</c>.</fsummary>
<desc><p><marker id="system_flag_scheduler_wall_time"></marker>
Turns on or off scheduler wall time measurements.</p>
@@ -6683,7 +6807,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="11"/>
+ <name name="system_flag" arity="2" clause_i="12"/>
<fsummary>Sets system flag <c>schedulers_online</c>.</fsummary>
<desc>
<p><marker id="system_flag_schedulers_online"></marker>
@@ -6708,7 +6832,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="12"/>
+ <name name="system_flag" arity="2" clause_i="13"/>
<fsummary>Sets system flag <c>trace_control_word</c>.</fsummary>
<desc>
<p>Sets the value of the node trace control word to
@@ -6722,7 +6846,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="12"/>
+ <name name="system_flag" arity="2" clause_i="14"/>
<fsummary>Finalize the Time Offset</fsummary>
<desc>
<p><marker id="system_flag_time_offset"></marker>
@@ -6839,11 +6963,7 @@ ok
As from <c>ERTS</c> 5.6.1, the return value is a list
of <c>{instance, InstanceNo, InstanceInfo}</c> tuples,
where <c>InstanceInfo</c> contains information about
- a specific instance of the allocator. As from
- <c>ERTS</c> 5.10.4, the returned list when calling
- <c>erlang:system_info({allocator, mseg_alloc})</c> also
- includes an <c>{erts_mmap, _}</c> tuple as one element
- in the list. If <c><anno>Alloc</anno></c> is not a
+ a specific instance of the allocator. If <c><anno>Alloc</anno></c> is not a
recognized allocator, <c>undefined</c> is returned.
If <c><anno>Alloc</anno></c> is disabled,
<c>false</c> is returned.</p>
@@ -6855,7 +6975,13 @@ ok
briefly documented.</p>
<p>The recognized allocators are listed in
<seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>.
- After reading the <c>erts_alloc(3)</c> documentation,
+ Information about super carriers can be obtained from
+ <c>ERTS</c> 8.0 with <c>{allocator, erts_mmap}</c> or from
+ <c>ERTS</c> 5.10.4, the returned list when calling with
+ <c>{allocator, mseg_alloc}</c> also includes an
+ <c>{erts_mmap, _}</c> tuple as one element in the list.</p>
+
+ <p>After reading the <c>erts_alloc(3)</c> documentation,
the returned information
more or less speaks for itself, but it can be worth
explaining some things. Call counts are presented by two
@@ -6987,6 +7113,81 @@ ok
</func>
<func>
+ <name name="system_info" arity="1" clause_i="27"/>
+ <name name="system_info" arity="1" clause_i="28"/>
+ <name name="system_info" arity="1" clause_i="36"/>
+ <name name="system_info" arity="1" clause_i="37"/>
+ <name name="system_info" arity="1" clause_i="38"/>
+ <name name="system_info" arity="1" clause_i="39"/>
+ <type name="message_queue_data"/>
+ <type name="max_heap_size"/>
+ <fsummary>Information about the default process heap settings.</fsummary>
+ <desc>
+ <taglist>
+ <tag><c>fullsweep_after</c></tag>
+ <item>
+ <p>Returns <c>{fullsweep_after, integer() >= 0}</c>, which is
+ the <c>fullsweep_after</c> garbage collection setting used
+ by default. For more information, see
+ <c>garbage_collection</c> described in the following.</p>
+ </item>
+ <tag><c>garbage_collection</c></tag>
+ <item>
+ <p>Returns a list describing the default garbage collection
+ settings. A process spawned on the local node by a
+ <c>spawn</c> or <c>spawn_link</c> uses these
+ garbage collection settings. The default settings can be
+ changed by using
+ <seealso marker="#system_flag/2">system_flag/2</seealso>.
+ <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>
+ can spawn a process that does not use the default
+ settings.</p>
+ </item>
+ <tag><c>max_heap_size</c></tag>
+ <item>
+ <p>Returns <c>{max_heap_size, <anno>MaxHeapSize</anno>}</c>,
+ where <c><anno>MaxHeapSize</anno></c> is the current
+ system-wide max heap size settings for spawned processes.
+ This setting can be set using the <c>erl</c> command line
+ flags <seealso marker="erl#+hmax"><c>+hmax</c></seealso>,
+ <seealso marker="erl#+hmaxk"><c>+hmaxk</c></seealso> and
+ <seealso marker="erl#+hmaxel"><c>+hmaxel</c></seealso>. It can
+ also be changed at run-time using
+ <seealso marker="#system_flag_max_heap_size">
+ <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
+ For more details about the <c>max_heap_size</c> process flag
+ see <seealso marker="#process_flag_max_heap_size">
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.
+ </p>
+ </item>
+ <tag><c>min_heap_size</c></tag>
+ <item>
+ <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>,
+ where <c><anno>MinHeapSize</anno></c> is the current
+ system-wide minimum heap size for spawned processes.</p>
+ </item>
+ <tag><marker id="system_info_message_queue_data"><c>message_queue_data</c></marker></tag>
+ <item>
+ <p>Returns the default value of the <c>message_queue_data</c>
+ process flag which is either <c>off_heap</c>, <c>on_heap</c>, or <c>mixed</c>.
+ This default is set by the <c>erl</c> command line argument
+ <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso>. For more information on the
+ <c>message_queue_data</c> process flag, see documentation of
+ <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data,
+ MQD)</c></seealso>.</p>
+ </item>
+ <tag><c>min_bin_vheap_size</c></tag>
+ <item>
+ <p>Returns <c>{min_bin_vheap_size,
+ <anno>MinBinVHeapSize</anno>}</c>, where
+ <c><anno>MinBinVHeapSize</anno></c> is the current system-wide
+ minimum binary virtual heap size for spawned processes.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
<name name="system_info" arity="1" clause_i="6"/>
<name name="system_info" arity="1" clause_i="7"/>
<name name="system_info" arity="1" clause_i="8"/>
@@ -7006,8 +7207,6 @@ ok
<name name="system_info" arity="1" clause_i="24"/>
<name name="system_info" arity="1" clause_i="25"/>
<name name="system_info" arity="1" clause_i="26"/>
- <name name="system_info" arity="1" clause_i="27"/>
- <name name="system_info" arity="1" clause_i="28"/>
<name name="system_info" arity="1" clause_i="29"/>
<name name="system_info" arity="1" clause_i="30"/>
<name name="system_info" arity="1" clause_i="31"/>
@@ -7015,10 +7214,6 @@ ok
<name name="system_info" arity="1" clause_i="33"/>
<name name="system_info" arity="1" clause_i="34"/>
<name name="system_info" arity="1" clause_i="35"/>
- <name name="system_info" arity="1" clause_i="36"/>
- <name name="system_info" arity="1" clause_i="37"/>
- <name name="system_info" arity="1" clause_i="38"/>
- <name name="system_info" arity="1" clause_i="39"/>
<name name="system_info" arity="1" clause_i="40"/>
<name name="system_info" arity="1" clause_i="41"/>
<name name="system_info" arity="1" clause_i="42"/>
@@ -7048,6 +7243,7 @@ ok
<name name="system_info" arity="1" clause_i="66"/>
<name name="system_info" arity="1" clause_i="67"/>
<name name="system_info" arity="1" clause_i="68"/>
+ <name name="system_info" arity="1" clause_i="69"/>
<fsummary>Information about the system.</fsummary>
<desc>
<p>Returns various information about the current system
@@ -7284,25 +7480,6 @@ ok
<c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
runtime system.</p>
</item>
- <tag><c>fullsweep_after</c></tag>
- <item>
- <p>Returns <c>{fullsweep_after, integer() >= 0}</c>, which is
- the <c>fullsweep_after</c> garbage collection setting used
- by default. For more information, see
- <c>garbage_collection</c> described in the following.</p>
- </item>
- <tag><c>garbage_collection</c></tag>
- <item>
- <p>Returns a list describing the default garbage collection
- settings. A process spawned on the local node by a
- <c>spawn</c> or <c>spawn_link</c> uses these
- garbage collection settings. The default settings can be
- changed by using
- <seealso marker="#system_flag/2">system_flag/2</seealso>.
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>
- can spawn a process that does not use the default
- settings.</p>
- </item>
<tag><c>heap_sizes</c></tag>
<item>
<p>Returns a list of integers representing valid heap sizes
@@ -7377,29 +7554,6 @@ ok
<item>
<p>Returns a string containing the Erlang machine name.</p>
</item>
- <tag><c>min_heap_size</c></tag>
- <item>
- <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>,
- where <c><anno>MinHeapSize</anno></c> is the current
- system-wide minimum heap size for spawned processes.</p>
- </item>
- <tag><marker id="system_info_message_queue_data"><c>message_queue_data</c></marker></tag>
- <item>
- <p>Returns the default value of the <c>message_queue_data</c>
- process flag which is either <c>off_heap</c>, <c>on_heap</c>, or <c>mixed</c>.
- This default is set by the <c>erl</c> command line argument
- <seealso marker="erl#+xmqd"><c>+xmqd</c></seealso>. For more information on the
- <c>message_queue_data</c> process flag, see documentation of
- <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data,
- MQD)</c></seealso>.</p>
- </item>
- <tag><c>min_bin_vheap_size</c></tag>
- <item>
- <p>Returns <c>{min_bin_vheap_size,
- <anno>MinBinVHeapSize</anno>}</c>, where
- <c><anno>MinBinVHeapSize</anno></c> is the current system-wide
- minimum binary virtual heap size for spawned processes.</p>
- </item>
<tag><c>modified_timing_level</c></tag>
<item>
<p>Returns the modified timing-level (an integer) if
@@ -7966,7 +8120,7 @@ ok
<c>stack_size</c>, <c>mbuf_size</c>, <c>old_heap_size</c>,
and <c>old_heap_block_size</c>. These tuples are
explained in the description of trace message
- <seealso marker="#gc_start">gc_start</seealso> (see
+ <seealso marker="#gc_minor_start">gc_minor_start</seealso> (see
<seealso marker="#trace/3">erlang:trace/3</seealso>).
New tuples can be added, and the order of the tuples in
the <c>Info</c> list can be changed at any time without
@@ -8024,12 +8178,13 @@ ok
<c>GcPid</c> and <c>Info</c>
are the same as for <c>long_gc</c> earlier, except that
the tuple tagged with <c>timeout</c> is not present.</p>
- <p>As of <c>ERTS</c> 5.6, the monitor message is sent
- if the sum of the sizes of all memory blocks allocated
- for all heap generations is equal to or higher than <c>Size</c>.
- Previously the monitor message was sent if the memory block
- allocated for the youngest generation was equal to or higher
- than <c>Size</c>.</p>
+ <p>The monitor message is sent if the sum of the sizes of
+ all memory blocks allocated for all heap generations after
+ a garbage collection is equal to or higher than <c>Size</c>.</p>
+ <p>When a process is killed by <seealso marker="#process_flag_max_heap_size">
+ <c>max_heap_size</c></seealso>, it is killed before the
+ garbage collection is complete and thus no large heap message
+ will be sent.</p>
</item>
<tag><c>busy_port</c></tag>
<item>
@@ -8556,7 +8711,9 @@ timestamp() ->
<tag><c>garbage_collection</c></tag>
<item>
<p>Traces garbage collections of processes.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_gc_start">gc_start</seealso></c> and <c><seealso marker="#trace_3_trace_messages_gc_end">gc_end</seealso></c>.</p>
+ <p>Message tags: <c><seealso marker="#trace_3_trace_messages_gc_minor_start">gc_minor_start</seealso></c>,
+ <c><seealso marker="#trace_3_trace_messages_gc_max_heap_size">gc_max_heap_size</seealso></c> and
+ <c><seealso marker="#trace_3_trace_messages_gc_minor_end">gc_minor_end</seealso></c>.</p>
</item>
<tag><c>timestamp</c></tag>
<item>
@@ -8637,8 +8794,8 @@ timestamp() ->
<p>Specifies that a tracer module should be called
instead of sending a trace message. The tracer module
can then ignore or change the trace message. For more details
- on how to write a tracer module see <seealso marker="erl_tracer">
- erl_tracer</seealso>
+ on how to write a tracer module see
+ <seealso marker="erts:erl_tracer"><c>erl_tracer</c></seealso>
</p>
</item>
</taglist>
@@ -8880,12 +9037,12 @@ timestamp() ->
</p>
</item>
<tag>
- <marker id="trace_3_trace_messages_gc_start"></marker>
- <c>{trace, Pid, gc_start, Info}</c>
+ <marker id="trace_3_trace_messages_gc_minor_start"></marker>
+ <c>{trace, Pid, gc_minor_start, Info}</c>
</tag>
<item>
- <marker id="gc_start"></marker>
- <p>Sent when garbage collection is about to be started.
+ <marker id="gc_minor_start"></marker>
+ <p>Sent when a young garbage collection is about to be started.
<c>Info</c> is a list of two-element tuples, where
the first element is a key, and the second is the value.
Do not depend on any order of the tuples.
@@ -8925,15 +9082,45 @@ timestamp() ->
<p>All sizes are in words.</p>
</item>
<tag>
- <marker id="trace_3_trace_messages_gc_end"></marker>
- <c>{trace, Pid, gc_end, Info}</c>
+ <marker id="trace_3_trace_messages_gc_max_heap_size"></marker>
+ <c>{trace, Pid, gc_max_heap_size, Info}</c>
</tag>
<item>
- <p>Sent when garbage collection is finished. <c>Info</c>
- contains the same kind of list as in message <c>gc_start</c>,
+ <p>
+ Sent when the <seealso marker="#process_flag_max_heap_size"><c>max_heap_size</c></seealso>
+ is reached during garbage collection. <c>Info</c> contains the
+ same kind of list as in message <c>gc_start</c>,
+ but the sizes reflect the sizes that triggered max_heap_size to
+ be reached.
+ </p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_gc_minor_end"></marker>
+ <c>{trace, Pid, gc_minor_end, Info}</c>
+ </tag>
+ <item>
+ <p>Sent when young garbage collection is finished. <c>Info</c>
+ contains the same kind of list as in message <c>gc_minor_start</c>,
but the sizes reflect the new sizes after
garbage collection.</p>
</item>
+ <tag>
+ <marker id="trace_3_trace_messages_gc_major_start"></marker>
+ <c>{trace, Pid, gc_major_start, Info}</c>
+ </tag>
+ <item>
+ <p>Sent when fullsweep garbage collection is about to be started. <c>Info</c>
+ contains the same kind of list as in message <c>gc_minor_start</c>.</p>
+ </item>
+ <tag>
+ <marker id="trace_3_trace_messages_gc_major_end"></marker>
+ <c>{trace, Pid, gc_major_end, Info}</c>
+ </tag>
+ <item>
+ <p>Sent when fullsweep garbage collection is finished. <c>Info</c>
+ contains the same kind of list as in message <c>gc_minor_start</c>
+ but the sizes reflect the new sizes after a fullsweep garbage collection.</p>
+ </item>
</taglist>
<p>If the tracing process/port dies or the tracer module returns
<c>remove</c>, the flags are silently removed.</p>
@@ -8989,7 +9176,7 @@ timestamp() ->
<c>erlang:trace_delivered(<anno>Tracee</anno>)</c> resides on.
The special <c><anno>Tracee</anno></c> atom <c>all</c>
denotes all processes that currently are traced in the node.</p>
- <p>When used together with an <seealso marker="#erl_tracer">
+ <p>When used together with an <seealso marker="erts:erl_tracer">
Tracer Module</seealso> any message sent in the trace callback
is guaranteed to have reached it's recipient before the
<c>trace_delivered</c> message is sent.</p>
@@ -9015,16 +9202,16 @@ timestamp() ->
<type name="trace_info_flag"/>
<type name="trace_match_spec"/>
<desc>
- <p>Returns trace information about a port, process or function.</p>
- <p>To get information about a port or process,
- <c><anno>PidPortOrFunc</anno></c> is to
+ <p>Returns trace information about a port, process, function or event.</p>
+ <p><em>To get information about a port or process</em>,
+ <c><anno>PidPortFuncEvent</anno></c> is to
be a process identifier (pid), port identifier or one of
the atoms <c>new</c>, <c>new_processes</c>, <c>new_ports</c>.
The atom <c>new</c> or <c>new_processes</c> means that the default trace
state for processes to be created is returned. The atom <c>new_ports</c>
means that the default trace state for ports to be created is returned.
</p>
- <p>The following <c>Item</c>s are valid:</p>
+ <p>The following <c>Item</c>s are valid for ports and processes:</p>
<taglist>
<tag><c>flags</c></tag>
<item>
@@ -9048,12 +9235,15 @@ timestamp() ->
value is <c>[]</c>.</p>
</item>
</taglist>
- <p>To get information about a function, <c><anno>PidPortOrFunc</anno></c> is to
+ <p><em>To get information about a function</em>, <c><anno>PidPortFuncEvent</anno></c> is to
be the three-element tuple <c>{Module, Function, Arity}</c> or
the atom <c>on_load</c>. No wild cards are allowed. Returns
<c>undefined</c> if the function does not exist, or
- <c>false</c> if the function is not traced.</p>
- <p>The following <c>Item</c>s are valid::</p>
+ <c>false</c> if the function is not traced. If <c><anno>PidPortFuncEvent</anno></c>
+ is <c>on_load</c>, the information returned refers to
+ the default value for code that will be loaded.</p>
+
+ <p>The following <c>Item</c>s are valid for functions:</p>
<taglist>
<tag><c>traced</c></tag>
<item>
@@ -9112,39 +9302,177 @@ timestamp() ->
is active for this function.</p>
</item>
</taglist>
+ <p><em>To get information about an event</em>, <c><anno>PidPortFuncEvent</anno></c> is to
+ be one of the atoms <c>send</c> or <c>'receive'</c>.</p>
+ <p>The only valid <c>Item</c> for events is:</p>
+ <taglist>
+ <tag><c>match_spec</c></tag>
+ <item>
+ <p>Returns the match specification for this event, if it
+ has one, or <c>true</c> if no match specification has been
+ set.</p>
+ </item>
+ </taglist>
<p>The return value is <c>{<anno>Item</anno>, Value}</c>, where
<c>Value</c> is the requested information as described earlier.
If a pid for a dead process was given, or the name of a
non-existing function, <c>Value</c> is <c>undefined</c>.</p>
- <p>If <c><anno>PidPortOrFunc</anno></c> is <c>on_load</c>, the information
- returned refers to the default value for code that will be
- loaded.</p>
</desc>
</func>
<func>
<name name="trace_pattern" arity="2" clause_i="1"/>
- <fsummary>Sets trace patterns for global call tracing.</fsummary>
+ <fsummary>Sets trace patterns for call, send or 'receive' tracing.</fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
<desc>
<p>The same as
- <seealso marker="#trace_pattern/3">erlang:trace_pattern(MFA, MatchSpec, [])</seealso>,
+ <seealso marker="#trace_pattern/3">erlang:trace_pattern(Event, MatchSpec, [])</seealso>,
retained for backward compatibility.</p>
</desc>
</func>
<func>
- <name name="trace_pattern" arity="3"/>
+ <name name="trace_pattern" arity="3" clause_i="1"/>
+ <fsummary>Sets trace pattern for message sending.</fsummary>
+ <type name="trace_match_spec"/>
+ <desc>
+ <p>Sets trace pattern for <em>message sending</em>.
+ Must be combined with
+ <seealso marker="#trace/3">erlang:trace/3</seealso>
+ to set the <c>send</c> trace flag for one or more processes.
+ By default all messages, sent from <c>send</c> traced processes,
+ are traced. Use <c>erlang:trace_pattern/3</c> to limit
+ traced send events based on the message content, the sender
+ and/or the receiver.</p>
+ <p>Argument <c><anno>MatchSpec</anno></c> can take the
+ following forms:</p>
+ <taglist>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
+ <item>
+ <p>A list of match specifications. The matching is done
+ on the list <c>[Receiver, Msg]</c>. <c>Receiver</c>
+ is the process or port identity of the receiver and
+ <c>Msg</c> is the message term. The pid of the sending
+ process can be accessed with the guard function
+ <c>self/0</c>. An empty list is the same as <c>true</c>.
+ See the users guide section
+ <seealso marker="erts:match_spec">Match Specifications in Erlang</seealso>
+ for more information.</p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>Enables tracing for all sent messages (from <c>send</c>
+ traced processes). Any match specification is
+ removed. <em>This is the default</em>.</p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>Disables tracing for all sent messages.
+ Any match specification is removed.</p>
+ </item>
+ </taglist>
+ <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c>
+ for send tracing.</p>
+ <p>The return value is always <c>1</c>.</p>
+ <p>Example; only trace messages to a specific process <c>Pid</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{[Pid, '_'],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages matching <c>{reply, _}</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['_', {reply,'_'}],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages sent to the sender itself:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['$1', '_'],[{'=:=','$1',{self}}],[]}], []).</input>
+1</pre>
+ <p>Only trace messages sent to other nodes:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['$1', '_'],[{'=/=',{node,'$1'},{node}}],[]}], []).</input>
+1</pre>
+ <note><p>A match specification for <c>send</c> trace can use
+ all guard and body functions except <c>caller</c>.</p></note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="trace_pattern" arity="3" clause_i="2"/>
+ <fsummary>Sets trace pattern for tracing of message receiving.</fsummary>
+ <type name="trace_match_spec"/>
+ <desc>
+ <p></p>
+ <p>Sets trace pattern for <em>message receiving</em>.
+ Must be combined with
+ <seealso marker="#trace/3">erlang:trace/3</seealso>
+ to set the <c>'receive'</c> trace flag for one or more processes.
+ By default all messages, received by <c>'receive'</c> traced processes,
+ are traced. Use <c>erlang:trace_pattern/3</c> to limit
+ traced receive events based on the message content, the sender
+ and/or the receiver.</p>
+ <p>Argument <c><anno>MatchSpec</anno></c> can take the
+ following forms:</p>
+ <taglist>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
+ <item>
+ <p>A list of match specifications. The matching is done
+ on the list <c>[Node, Sender, Msg]</c>. <c>Node</c>
+ is the node name of the sender. <c>Sender</c> is the
+ process or port identity of the sender, or the atom
+ <c>undefined</c> if the sender is not known (which may
+ be the case for remote senders). <c>Msg</c> is the
+ message term. The pid of the receiving process can be
+ accessed with the guard function <c>self/0</c>. An empty
+ list is the same as <c>true</c>. See the users guide section
+ <seealso marker="erts:match_spec">Match Specifications in Erlang</seealso>
+ for more information.</p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>Enables tracing for all received messages (to <c>'receive'</c>
+ traced processes). Any match specification is
+ removed. <em>This is the default</em>.</p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>Disables tracing for all received messages.
+ Any match specification is removed.</p>
+ </item>
+ </taglist>
+ <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c>
+ for receive tracing.</p>
+ <p>The return value is always <c>1</c>.</p>
+ <p>Example; only trace messages from a specific process <c>Pid</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['_',Pid, '_'],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages matching <c>{reply, _}</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['_','_', {reply,'_'}],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages from other nodes:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['$1', '_', '_'],[{'=/=','$1',{node}}],[]}], []).</input>
+1</pre>
+ <note><p>A match specification for <c>'receive'</c> trace can
+ use all guard and body functions except <c>caller,
+ is_seq_trace, get_seq_token, set_seq_token, enable_trace,
+ disable_trace, trace, silent</c> and <c>process_dump</c>.</p></note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="trace_pattern" arity="3" clause_i="3"/>
<fsummary>Sets trace patterns for tracing of function calls.</fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
<type name="trace_pattern_flag"/>
<desc>
- <p>Enables or disables call tracing for
- one or more functions. Must be combined with
+ <p>Enables or disables <em>call tracing</em> for one or more functions.
+ Must be combined with
<seealso marker="#trace/3">erlang:trace/3</seealso>
- to set the <c>call</c> trace flag for one or more processes.</p>
+ to set the <c>call</c> trace flag
+ for one or more processes.</p>
<p>Conceptually, call tracing works as follows. Inside
the Erlang Virtual Machine, a set of processes and
a set of functions are to be traced. If a traced process
@@ -9199,7 +9527,8 @@ timestamp() ->
</item>
<tag><c>true</c></tag>
<item>
- <p>Enables tracing for the matching functions.</p>
+ <p>Enables tracing for the matching functions.
+ Any match specification is removed.</p>
</item>
<tag><c><anno>MatchSpecList</anno></c></tag>
<item>
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 8c51f788c0..8f65e71531 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -273,6 +273,11 @@ atom garbage_collecting
atom garbage_collection
atom garbage_collection_info
atom gc_end
+atom gc_major_end
+atom gc_major_start
+atom gc_max_heap_size
+atom gc_minor_end
+atom gc_minor_start
atom gc_start
atom Ge='>='
atom generational
@@ -362,6 +367,7 @@ atom match_spec
atom match_spec_result
atom max
atom maximum
+atom max_heap_size
atom max_tables max_processes
atom mbuf_size
atom md5
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 87508dcf5f..40d44dda4c 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -38,7 +38,7 @@
#include "erl_thr_progress.h"
static void set_default_trace_pattern(Eterm module);
-static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp);
+static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls);
static void delete_code(Module* modp);
static void decrement_refc(BeamCodeHeader*);
static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
@@ -467,7 +467,7 @@ check_old_code_1(BIF_ALIST_1)
}
Eterm
-erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp)
+erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls)
{
Module* modp;
Eterm res;
@@ -483,7 +483,7 @@ erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp)
return am_false;
erts_rlock_old_code(code_ix);
res = (!modp->old.code_hdr ? am_false :
- check_process_code(c_p, modp, flags, redsp));
+ check_process_code(c_p, modp, flags, redsp, fcalls));
erts_runlock_old_code(code_ix);
return res;
@@ -506,7 +506,7 @@ BIF_RETTYPE erts_internal_check_process_code_2(BIF_ALIST_2)
goto badarg;
}
- res = erts_check_process_code(BIF_P, BIF_ARG_1, flags, &reds);
+ res = erts_check_process_code(BIF_P, BIF_ARG_1, flags, &reds, BIF_P->fcalls);
ASSERT(is_value(res));
@@ -625,8 +625,8 @@ BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1)
{
Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
- if (modp && modp->curr.code_hdr) {
- BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code_hdr->on_load_function_ptr);
+ if (modp && modp->old.code_hdr) {
+ BIF_TRAP_CODE_PTR_0(BIF_P, modp->old.code_hdr->on_load_function_ptr);
}
else {
BIF_ERROR(BIF_P, BADARG);
@@ -651,14 +651,14 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
code_ix = erts_active_code_ix();
modp = erts_get_module(BIF_ARG_1, code_ix);
- if (!modp || !modp->curr.code_hdr) {
+ if (!modp || !modp->old.code_hdr) {
error:
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_release_code_write_permission();
BIF_ERROR(BIF_P, BADARG);
}
- if (modp->curr.code_hdr->on_load_function_ptr == NULL) {
+ if (modp->old.code_hdr->on_load_function_ptr == NULL) {
goto error;
}
if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
@@ -667,44 +667,55 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
if (BIF_ARG_2 == am_true) {
int i;
+ struct erl_module_instance t;
+
+ /*
+ * Swap old and new code.
+ */
+ t = modp->curr;
+ modp->curr = modp->old;
+ modp->old = t;
/*
* The on_load function succeded. Fix up export entries.
*/
for (i = 0; i < export_list_size(code_ix); i++) {
Export *ep = export_list(i,code_ix);
- if (ep != NULL &&
- ep->code[0] == BIF_ARG_1 &&
- ep->code[4] != 0) {
+ if (ep == NULL || ep->code[0] != BIF_ARG_1) {
+ continue;
+ }
+ if (ep->code[4] != 0) {
ep->addressv[code_ix] = (void *) ep->code[4];
ep->code[4] = 0;
+ } else {
+ if (ep->addressv[code_ix] == ep->code+3 &&
+ ep->code[3] == (BeamInstr) em_apply_bif) {
+ continue;
+ }
+ ep->addressv[code_ix] = ep->code+3;
+ ep->code[3] = (BeamInstr) em_call_error_handler;
}
}
modp->curr.code_hdr->on_load_function_ptr = NULL;
set_default_trace_pattern(BIF_ARG_1);
} else if (BIF_ARG_2 == am_false) {
- BeamInstr* code;
- BeamInstr* end;
+ int i;
/*
- * The on_load function failed. Remove the loaded code.
- * This is an combination of delete and purge. We purge
- * the current code; the old code is not touched.
+ * The on_load function failed. Remove references to the
+ * code that is about to be purged from the export entries.
*/
- erts_total_code_size -= modp->curr.code_length;
- code = (BeamInstr*) modp->curr.code_hdr;
- end = (BeamInstr *) ((char *)code + modp->curr.code_length);
- erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length,
- erts_active_code_ix());
- if (modp->curr.code_hdr->literals_start) {
- erts_free(ERTS_ALC_T_LITERAL, modp->curr.code_hdr->literals_start);
- }
- erts_free(ERTS_ALC_T_CODE, modp->curr.code_hdr);
- modp->curr.code_hdr = NULL;
- modp->curr.code_length = 0;
- modp->curr.catches = BEAM_CATCHES_NIL;
- erts_remove_from_ranges(code);
+
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export *ep = export_list(i,code_ix);
+ if (ep == NULL || ep->code[0] != BIF_ARG_1) {
+ continue;
+ }
+ if (ep->code[3] == (BeamInstr) em_apply_bif) {
+ continue;
+ }
+ ep->code[4] = 0;
+ }
}
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
@@ -753,7 +764,7 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size)
static Eterm
-check_process_code(Process* rp, Module* modp, Uint flags, int *redsp)
+check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
{
BeamInstr* start;
char* literals;
@@ -955,7 +966,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp)
if (need_gc & ERTS_ORDINARY_GC__) {
FLAGS(rp) |= F_NEED_FULLSWEEP;
- *redsp += erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity);
+ *redsp += erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity, fcalls);
done_gc |= ERTS_ORDINARY_GC__;
}
if (need_gc & ERTS_LITERAL_GC__) {
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index 2ee98ed7b5..1e30e8d8d1 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -248,7 +248,10 @@ erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified)
void
erts_bp_free_matched_functions(BpFunctions* f)
{
- Free(f->matching);
+ if (f->matching) {
+ Free(f->matching);
+ }
+ else ASSERT(f->matched == 0);
}
void
@@ -1432,7 +1435,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
g = (GenericBp *) pc[-4];
if (g == 0) {
int i;
- if (count_op == erts_break_reset || count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_RESTART || count_op == ERTS_BREAK_PAUSE) {
/* Do not insert a new breakpoint */
return;
}
@@ -1456,7 +1459,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
MatchSetUnref(bp->meta_ms);
bp_meta_unref(bp->meta_tracer);
} else if (common & ERTS_BPF_COUNT) {
- if (count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_PAUSE) {
bp->flags &= ~ERTS_BPF_COUNT_ACTIVE;
} else {
bp->flags |= ERTS_BPF_COUNT_ACTIVE;
@@ -1468,7 +1471,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
BpDataTime* bdt = bp->time;
Uint i = 0;
- if (count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_PAUSE) {
bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE;
} else {
bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE;
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index bb171be8a6..9c2fc007a2 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -86,10 +86,10 @@ typedef struct generic_bp {
#endif
enum erts_break_op{
- erts_break_nop = 0, /* Must be false */
- erts_break_set = !0, /* Must be true */
- erts_break_reset,
- erts_break_stop
+ ERTS_BREAK_NOP = 0, /* Must be false */
+ ERTS_BREAK_SET = !0, /* Must be true */
+ ERTS_BREAK_RESTART,
+ ERTS_BREAK_PAUSE
};
typedef Uint32 ErtsBpIndex;
@@ -173,19 +173,7 @@ void erts_clear_time_trace_bif(BeamInstr *pc);
BeamInstr *erts_find_local_func(Eterm mfa[3]);
-ERTS_GLB_INLINE Uint erts_bp_sched2ix(void);
-
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE Uint erts_bp_sched2ix(void)
-{
-#ifdef ERTS_SMP
- ErtsSchedulerData *esdp;
- esdp = erts_get_scheduler_data();
- return esdp->no - 1;
-#else
- return 0;
-#endif
-}
extern erts_smp_atomic32_t erts_active_bp_index;
extern erts_smp_atomic32_t erts_staging_bp_index;
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 72526bca5e..d00a563087 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -308,7 +308,8 @@ void** beam_ops;
if (E - HTOP < (needed + (HeapNeed))) { \
SWAPOUT; \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect_nobump(c_p, needed + (HeapNeed), reg, (M)); \
+ FCALLS -= erts_garbage_collect_nobump(c_p, needed + (HeapNeed), \
+ reg, (M), FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
SWAPIN; \
@@ -360,7 +361,7 @@ void** beam_ops;
if ((E - HTOP < need) || (MSO(c_p).overhead + (VNh) >= BIN_VHEAP_SZ(c_p))) {\
SWAPOUT; \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)); \
+ FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live), FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
SWAPIN; \
@@ -381,7 +382,7 @@ void** beam_ops;
if (E - HTOP < need) { \
SWAPOUT; \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live));\
+ FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live), FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
SWAPIN; \
@@ -402,7 +403,7 @@ void** beam_ops;
SWAPOUT; \
reg[Live] = Extra; \
PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)+1); \
+ FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)+1, FCALLS); \
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
Extra = reg[Live]; \
@@ -1196,6 +1197,25 @@ init_emulator(void)
#define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0)
#endif /* USE_VM_PROBES */
+#ifdef DEBUG
+#define ERTS_DBG_CHK_REDS(P, FC) \
+ do { \
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) { \
+ ASSERT(FC <= 0); \
+ ASSERT(ERTS_PROC_GET_SCHDATA(c_p)->virtual_reds \
+ <= 0 - (FC)); \
+ } \
+ else { \
+ ASSERT(FC <= CONTEXT_REDS); \
+ ASSERT(ERTS_PROC_GET_SCHDATA(c_p)->virtual_reds \
+ <= CONTEXT_REDS - (FC)); \
+ } \
+} while (0)
+#else
+#define ERTS_DBG_CHK_REDS(P, FC)
+#endif
+
+
/*
* process_main() is called twice:
* The first call performs some initialisation, including exporting
@@ -1290,7 +1310,12 @@ void process_main(void)
goto do_schedule1;
do_schedule:
- reds_used = REDS_IN(c_p) - FCALLS;
+ ASSERT(c_p->debug_reds_in == REDS_IN(c_p));
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ reds_used = REDS_IN(c_p) - FCALLS;
+ else
+ reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS);
+ ASSERT(reds_used >= 0);
do_schedule1:
if (start_time != 0) {
@@ -1310,6 +1335,7 @@ void process_main(void)
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
c_p = schedule(c_p, reds_used);
+ ASSERT(!(c_p->flags & F_HIPE_MODE));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
start_time = 0;
#ifdef DEBUG
@@ -1348,16 +1374,21 @@ void process_main(void)
SET_I(c_p->i);
- reds = c_p->fcalls;
- if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)
- && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) {
- neg_o_reds = -reds;
- FCALLS = REDS_IN(c_p) = 0;
+ REDS_IN(c_p) = reds = c_p->fcalls;
+#ifdef DEBUG
+ c_p->debug_reds_in = reds;
+#endif
+
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) {
+ neg_o_reds = -CONTEXT_REDS;
+ FCALLS = neg_o_reds + reds;
} else {
neg_o_reds = 0;
- FCALLS = REDS_IN(c_p) = reds;
+ FCALLS = reds;
}
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
+
next = (BeamInstr *) *I;
SWAPIN;
ASSERT(VALID_INSTR(next));
@@ -1673,6 +1704,14 @@ void process_main(void)
BeamInstr *next;
Eterm result;
+ if (!(FCALLS > 0 || FCALLS > neg_o_reds)) {
+ /* If we have run out of reductions, we do a context
+ switch before calling the bif */
+ c_p->arity = 2;
+ c_p->current = NULL;
+ goto context_switch3;
+ }
+
PRE_BIF_SWAPOUT(c_p);
c_p->fcalls = FCALLS - 1;
result = erl_send(c_p, r(0), x(1));
@@ -1770,7 +1809,7 @@ void process_main(void)
if (E - HTOP < 3) {
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1);
+ FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1, FCALLS);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
SWAPIN;
@@ -1865,6 +1904,7 @@ void process_main(void)
c_p->flags |= F_DELAY_GC;
loop_rec__:
+
PROCESS_MAIN_CHK_LOCKS(c_p);
msgp = PEEK_MESSAGE(c_p);
@@ -2007,6 +2047,8 @@ void process_main(void)
ERTS_VBUMP_LEAVE_REDS_INTERNAL(c_p, 5, FCALLS);
}
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
+
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
NextPF(0, next);
@@ -2166,7 +2208,7 @@ void process_main(void)
PreFetch(0, next);
if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) {
- trace_receive(c_p, am_timeout);
+ trace_receive(c_p, am_clock_service, am_timeout, NULL);
}
if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) {
save_calls(c_p, &exp_timeout);
@@ -2535,6 +2577,7 @@ do { \
GetArg1(2, tmp_reg[0]);
bf = (BifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
@@ -2544,6 +2587,7 @@ do { \
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(3, result);
}
@@ -2564,6 +2608,7 @@ do { \
GetArg1(1, tmp_reg[0]);
bf = (BifFunction) Arg(0);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
@@ -2573,6 +2618,7 @@ do { \
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(2, result);
}
@@ -2591,6 +2637,7 @@ do { \
GetArg1(2, x(live));
bf = (GcBifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2602,6 +2649,7 @@ do { \
SWAPIN;
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(4, result);
}
@@ -2630,6 +2678,7 @@ do { \
*/
live++;
bf = (GcBifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2641,6 +2690,7 @@ do { \
SWAPIN;
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(5, result);
}
@@ -2671,6 +2721,7 @@ do { \
*/
live += 2;
bf = (GcBifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2682,6 +2733,7 @@ do { \
SWAPIN;
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(5, result);
}
@@ -2708,6 +2760,7 @@ do { \
GetArg2(2, tmp_reg[0], tmp_reg[1]);
bf = (BifFunction) Arg(1);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
@@ -2717,6 +2770,7 @@ do { \
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_HOLE_CHECK(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(result)) {
StoreBifResult(4, result);
}
@@ -2764,6 +2818,15 @@ do { \
BeamInstr *next;
ErlHeapFragment *live_hf_end;
+
+ if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
+ /* If we have run out of reductions, we do a context
+ switch before calling the bif */
+ c_p->arity = ((Export *)Arg(0))->code[2];
+ c_p->current = ((Export *)Arg(0))->code;
+ goto context_switch3;
+ }
+
if (ERTS_MSACC_IS_ENABLED_CACHED_X()) {
if (GET_BIF_MODULE(Arg(0)) == am_ets) {
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS);
@@ -2775,6 +2838,7 @@ do { \
bf = GET_BIF_ADDRESS(Arg(0));
PRE_BIF_SWAPOUT(c_p);
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS - 1;
if (FCALLS <= 0) {
save_calls(c_p, (Export *) Arg(0));
@@ -2796,6 +2860,7 @@ do { \
PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
/* We have to update the cache if we are enabled in order
to make sure no book keeping is done after we disabled
msacc. We don't always do this as it is quite expensive. */
@@ -3290,10 +3355,19 @@ do { \
context_switch2: /* Entry for fun calls. */
c_p->current = I-3; /* Pointer to Mod, Func, Arity */
+ context_switch3:
+
{
Eterm* argp;
int i;
+ if (erts_smp_atomic32_read_nob(&c_p->state) & ERTS_PSFLG_EXITING) {
+ c_p->i = beam_exit;
+ c_p->arity = 0;
+ c_p->current = NULL;
+ goto do_schedule;
+ }
+
/*
* Make sure that there is enough room for the argument registers to be saved.
*/
@@ -3324,8 +3398,13 @@ do { \
* (beacuse the code for the Dispatch() macro becomes shorter that way).
*/
- reds_used = REDS_IN(c_p) - FCALLS + 1;
-
+ ASSERT(c_p->debug_reds_in == REDS_IN(c_p));
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ reds_used = REDS_IN(c_p) - FCALLS;
+ else
+ reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS);
+ ASSERT(reds_used >= 0);
+
/*
* Save the argument registers and everything else.
*/
@@ -3456,6 +3535,12 @@ do { \
BifFunction vbf;
ErlHeapFragment *live_hf_end;
+ if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) {
+ /* If we have run out of reductions, we do a context
+ switch before calling the nif */
+ goto context_switch;
+ }
+
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
@@ -3498,6 +3583,13 @@ do { \
* code[3]: &&apply_bif
* code[4]: Function pointer to BIF function
*/
+
+ if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) {
+ /* If we have run out of reductions, we do a context
+ switch before calling the bif */
+ goto context_switch;
+ }
+
if (ERTS_MSACC_IS_ENABLED_CACHED_X()) {
if ((Eterm)I[-3] == am_ets) {
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS);
@@ -3514,6 +3606,7 @@ do { \
DTRACE_BIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
SWAPOUT;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS - 1);
c_p->fcalls = FCALLS - 1;
vbf = (BifFunction) Arg(0);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -3549,6 +3642,7 @@ do { \
}
SWAPIN; /* There might have been a garbage collection. */
FCALLS = c_p->fcalls;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
if (is_value(nif_bif_result)) {
r(0) = nif_bif_result;
CHECK_TERM(r(0));
@@ -4611,9 +4705,9 @@ do { \
OpCase(i_generic_breakpoint): {
BeamInstr real_I;
ASSERT(I[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- SWAPOUT;
+ HEAVY_SWAPOUT;
real_I = erts_generic_breakpoint(c_p, I, reg);
- SWAPIN;
+ HEAVY_SWAPIN;
ASSERT(VALID_INSTR(real_I));
Goto(real_I);
}
@@ -4790,6 +4884,7 @@ do { \
{
#define HIPE_MODE_SWITCH(Cmd) \
SWAPOUT; \
+ ERTS_DBG_CHK_REDS(c_p, FCALLS); \
c_p->fcalls = FCALLS; \
c_p->def_arg_reg[4] = -neg_o_reds; \
c_p = hipe_mode_switch(c_p, Cmd, reg); \
@@ -4828,6 +4923,9 @@ do { \
#undef HIPE_MODE_SWITCH
L_post_hipe_mode_switch:
+#ifdef DEBUG
+ pid = c_p->common.id; /* may have switched process... */
+#endif
reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array;
freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array;
ERL_BITS_RELOAD_STATEP(c_p);
@@ -4835,6 +4933,7 @@ do { \
neg_o_reds = -c_p->def_arg_reg[4];
FCALLS = c_p->fcalls;
SWAPIN;
+ ERTS_DBG_CHK_REDS(c_p, FCALLS);
switch( c_p->def_arg_reg[3] ) {
case HIPE_MODE_SWITCH_RES_RETURN:
ASSERT(is_value(reg[0]));
@@ -6839,7 +6938,7 @@ erts_current_reductions(Process *current, Process *p)
if (current != p) {
return 0;
} else if (current->fcalls < 0 && ERTS_PROC_GET_SAVED_CALLS_BUF(current)) {
- return -current->fcalls;
+ return current->fcalls + CONTEXT_REDS;
} else {
return REDS_IN(current) - current->fcalls;
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 2e21a553ed..95489d9a63 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -32,6 +32,7 @@
#include "bif.h"
#include "external.h"
#include "beam_load.h"
+#include "beam_bp.h"
#include "big.h"
#include "erl_bits.h"
#include "beam_catches.h"
@@ -479,9 +480,9 @@ static void free_loader_state(Binary* magic);
static ErlHeapFragment* new_literal_fragment(Uint size);
static void free_literal_fragment(ErlHeapFragment*);
static void loader_state_dtor(Binary* magic);
-static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm group_leader, Eterm module,
- BeamCodeHeader* code, Uint size);
+static Eterm stub_insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
+ Eterm group_leader, Eterm module,
+ BeamCodeHeader* code, Uint size);
static int init_iff_file(LoaderState* stp, byte* code, Uint size);
static int scan_iff_file(LoaderState* stp, Uint* chunk_types,
Uint num_types, Uint num_mandatory);
@@ -513,7 +514,7 @@ static GenOp* gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
static int freeze_code(LoaderState* stp);
-static void final_touch(LoaderState* stp);
+static void final_touch(LoaderState* stp, struct erl_module_instance* inst_p);
static void short_file(int line, LoaderState* stp, unsigned needed);
static void load_printf(int line, LoaderState* context, char *fmt, ...);
static int transform_engine(LoaderState* st);
@@ -766,8 +767,11 @@ Eterm
erts_finish_loading(Binary* magic, Process* c_p,
ErtsProcLocks c_p_locks, Eterm* modp)
{
- Eterm retval;
+ Eterm retval = NIL;
LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic);
+ Module* mod_tab_p;
+ struct erl_module_instance* inst_p;
+ Uint size;
/*
* No other process may run since we will update the export
@@ -776,27 +780,80 @@ erts_finish_loading(Binary* magic, Process* c_p,
ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_has_code_write_permission() ||
erts_smp_thr_progress_is_blocking());
-
/*
* Make current code for the module old and insert the new code
* as current. This will fail if there already exists old code
* for the module.
*/
+ mod_tab_p = erts_put_module(stp->module);
CHKBLK(ERTS_ALC_T_CODE,stp->code);
- retval = insert_new_code(c_p, c_p_locks, stp->group_leader, stp->module,
- stp->hdr, stp->loaded_size);
- if (retval != NIL) {
- goto load_error;
+ if (!stp->on_load) {
+ /*
+ * Normal case -- no -on_load() function.
+ */
+ retval = beam_make_current_old(c_p, c_p_locks, stp->module);
+ ASSERT(retval == NIL);
+ } else {
+ ErtsCodeIndex code_ix = erts_staging_code_ix();
+ Eterm module = stp->module;
+ int i;
+
+ /*
+ * There is an -on_load() function. We will keep the current
+ * code, but we must turn off any tracing.
+ */
+
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export *ep = export_list(i, code_ix);
+ if (ep == NULL || ep->code[0] != module) {
+ continue;
+ }
+ if (ep->addressv[code_ix] == ep->code+3) {
+ if (ep->code[3] == (BeamInstr) em_apply_bif) {
+ continue;
+ } else if (ep->code[3] ==
+ (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
+ ASSERT(mod_tab_p->curr.num_traced_exports > 0);
+ erts_clear_export_break(mod_tab_p, ep->code+3);
+ ep->addressv[code_ix] = (BeamInstr *) ep->code[4];
+ ep->code[4] = 0;
+ }
+ ASSERT(ep->code[4] == 0);
+ }
+ }
+ ASSERT(mod_tab_p->curr.num_breakpoints == 0);
+ ASSERT(mod_tab_p->curr.num_traced_exports == 0);
}
/*
+ * Update module table.
+ */
+
+ size = stp->loaded_size;
+ erts_total_code_size += size;
+ if (stp->on_load) {
+ inst_p = &mod_tab_p->old;
+ } else {
+ inst_p = &mod_tab_p->curr;
+ }
+ inst_p->code_hdr = stp->hdr;
+ inst_p->code_length = size;
+
+ /*
+ * Update ranges (used for finding a function from a PC value).
+ */
+
+ erts_update_ranges((BeamInstr*)inst_p->code_hdr, size);
+
+ /*
* Ready for the final touch: fixing the export table entries for
* exported and imported functions. This can't fail.
*/
CHKBLK(ERTS_ALC_T_CODE,stp->code);
- final_touch(stp);
+ final_touch(stp, inst_p);
/*
* Loading succeded.
@@ -820,7 +877,6 @@ erts_finish_loading(Binary* magic, Process* c_p,
retval = am_on_load;
}
- load_error:
free_loader_state(magic);
return retval;
}
@@ -1026,9 +1082,9 @@ loader_state_dtor(Binary* magic)
}
static Eterm
-insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm group_leader, Eterm module, BeamCodeHeader* code_hdr,
- Uint size)
+stub_insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
+ Eterm group_leader, Eterm module,
+ BeamCodeHeader* code_hdr, Uint size)
{
Module* modp;
Eterm retval;
@@ -4700,14 +4756,13 @@ freeze_code(LoaderState* stp)
}
static void
-final_touch(LoaderState* stp)
+final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
{
int i;
int on_load = stp->on_load;
unsigned catches;
Uint index;
BeamInstr* codev = stp->codev;
- Module* modp;
/*
* Allocate catch indices and fix up all catch_yf instructions.
@@ -4722,8 +4777,7 @@ final_touch(LoaderState* stp)
codev[index+2] = make_catch(catches);
index = next;
}
- modp = erts_put_module(stp->module);
- modp->curr.catches = catches;
+ inst_p->catches = catches;
/*
* Export functions.
@@ -4743,10 +4797,10 @@ final_touch(LoaderState* stp)
ep->addressv[erts_staging_code_ix()] = address;
} else {
/*
- * Don't make any of the exported functions
- * callable yet.
+ * on_load: Don't make any of the exported functions
+ * callable yet. Keep any function in the current
+ * code callable.
*/
- ep->addressv[erts_staging_code_ix()] = ep->code+3;
ep->code[4] = (BeamInstr) address;
}
}
@@ -6421,7 +6475,8 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Insert the module in the module table.
*/
- rval = insert_new_code(p, 0, p->group_leader, Mod, code_hdr, code_size);
+ rval = stub_insert_new_code(p, 0, p->group_leader, Mod,
+ code_hdr, code_size);
if (rval != NIL) {
goto error;
}
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index ed5b2983dd..483c5320d7 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -44,6 +44,7 @@
#include "erl_ptab.h"
#include "erl_bits.h"
#include "erl_bif_unique.h"
+#include "erl_map.h"
#include "erl_msacc.h"
Export *erts_await_result;
@@ -611,7 +612,7 @@ erts_queue_monitor_message(Process *p,
ref_copy = copy_struct(ref, ref_size, &hp, ohp);
tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy);
- erts_queue_message(p, p_locksp, msgp, tup);
+ erts_queue_message(p, *p_locksp, msgp, tup, am_system);
}
static BIF_RETTYPE
@@ -880,6 +881,8 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
so.flags = erts_default_spo_flags|SPO_USE_ARGS;
so.min_heap_size = H_MIN_SIZE;
so.min_vheap_size = BIN_VH_MIN_SIZE;
+ so.max_heap_size = H_MAX_SIZE;
+ so.max_heap_flags = H_MAX_FLAGS;
so.priority = PRIORITY_NORMAL;
so.max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
so.scheduler = 0;
@@ -937,6 +940,9 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
} else {
so.min_heap_size = erts_next_heap_size(min_heap_size, 0);
}
+ } else if (arg == am_max_heap_size) {
+ if (!erts_max_heap_size(val, &so.max_heap_size, &so.max_heap_flags))
+ goto error;
} else if (arg == am_min_bin_vheap_size && is_small(val)) {
Sint min_vheap_size = signed_val(val);
if (min_vheap_size < 0) {
@@ -970,6 +976,10 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
goto error;
}
+ if (so.max_heap_size != 0 && so.max_heap_size < so.min_heap_size) {
+ goto error;
+ }
+
/*
* Spawn the process.
*/
@@ -1569,7 +1579,32 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P,
scb->n = 0;
}
- scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, scb);
+#ifdef HIPE
+ if (rp->flags & F_HIPE_MODE) {
+ ASSERT(!ERTS_PROC_GET_SAVED_CALLS_BUF(rp));
+ scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(rp, scb);
+ }
+ else
+#endif
+ {
+#ifdef HIPE
+ ASSERT(!ERTS_PROC_GET_SUSPENDED_SAVED_CALLS_BUF(rp));
+#endif
+ scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, scb);
+ if (rp == BIF_P && ((scb && i == 0) || (!scb && i != 0))) {
+ /* Adjust fcalls to match save calls setting... */
+ if (i == 0)
+ BIF_P->fcalls += CONTEXT_REDS; /* disabled it */
+ else
+ BIF_P->fcalls -= CONTEXT_REDS; /* enabled it */
+
+ /*
+ * Make sure we reschedule immediately so the
+ * change take effect at once.
+ */
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ }
+ }
if (!scb)
old_value = make_small(0);
@@ -1578,12 +1613,7 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P,
erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb);
}
- /* Make sure the process in question is rescheduled
- immediately, if it's us, so the call saving takes effect. */
- if (rp == BIF_P)
- BIF_RET2(old_value, CONTEXT_REDS);
- else
- BIF_RET(old_value);
+ BIF_RET(old_value);
}
error:
@@ -1711,6 +1741,23 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
}
BIF_RET(old_value);
}
+ else if (BIF_ARG_1 == am_max_heap_size) {
+ Eterm *hp;
+ Uint sz = 0, max_heap_size, max_heap_flags;
+
+ if (!erts_max_heap_size(BIF_ARG_2, &max_heap_size, &max_heap_flags))
+ goto error;
+
+ if ((max_heap_size < MIN_HEAP_SIZE(BIF_P) && max_heap_size != 0))
+ goto error;
+
+ erts_max_heap_size_map(MAX_HEAP_SIZE_GET(BIF_P), MAX_HEAP_SIZE_FLAGS_GET(BIF_P), NULL, &sz);
+ hp = HAlloc(BIF_P, sz);
+ old_value = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(BIF_P), MAX_HEAP_SIZE_FLAGS_GET(BIF_P), &hp, NULL);
+ MAX_HEAP_SIZE_SET(BIF_P, max_heap_size);
+ MAX_HEAP_SIZE_FLAGS_SET(BIF_P, max_heap_flags);
+ BIF_RET(old_value);
+ }
else if (BIF_ARG_1 == am_message_queue_data) {
old_value = erts_change_message_queue_management(BIF_P, BIF_ARG_2);
if (is_non_value(old_value))
@@ -1736,7 +1783,9 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
ERTS_TRACE_FLAGS(BIF_P) &= ~F_SENSITIVE;
}
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_ALL_MINOR);
- BIF_RET(old_value);
+ /* make sure to bump all reds so that we get
+ rescheduled immediately so setting takes effect */
+ BIF_RET2(old_value, CONTEXT_REDS);
}
else if (BIF_ARG_1 == am_monitor_nodes) {
/*
@@ -1775,10 +1824,18 @@ BIF_RETTYPE process_flag_3(BIF_ALIST_3)
Process *rp;
Eterm res;
- if ((rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, ERTS_PROC_LOCK_MAIN)) == NULL) {
+#ifdef ERTS_SMP
+ rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, ERTS_PROC_LOCK_MAIN);
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD3(bif_export[BIF_process_flag_3], BIF_P,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+#else
+ rp = erts_proc_lookup(BIF_ARG_1);
+#endif
+
+ if (!rp)
BIF_ERROR(BIF_P, BADARG);
- }
res = process_flag_aux(BIF_P, rp, BIF_ARG_2, BIF_ARG_3);
@@ -4320,6 +4377,31 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
BIF_RET(make_small(oval));
+ } else if (BIF_ARG_1 == am_max_heap_size) {
+
+ Eterm *hp, old_value;
+ Uint sz = 0, max_heap_size, max_heap_flags;
+
+ if (!erts_max_heap_size(BIF_ARG_2, &max_heap_size, &max_heap_flags))
+ goto error;
+
+ if (max_heap_size < H_MIN_SIZE && max_heap_size != 0)
+ goto error;
+
+ erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, NULL, &sz);
+ hp = HAlloc(BIF_P, sz);
+ old_value = erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, &hp, NULL);
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+
+ H_MAX_SIZE = max_heap_size;
+ H_MAX_FLAGS = max_heap_flags;
+
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ BIF_RET(old_value);
} else if (BIF_ARG_1 == am_display_items) {
int oval = display_items;
if (!is_small(BIF_ARG_2) || (n = signed_val(BIF_ARG_2)) < 0) {
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index 546e3830b9..5d751dd67d 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -52,6 +52,7 @@ extern Export *erts_convert_time_unit_trap;
(p)->fcalls = 0; \
else \
(p)->fcalls = -CONTEXT_REDS; \
+ ASSERT(ERTS_BIF_REDS_LEFT((p)) == 0); \
} while(0)
#define ERTS_VBUMP_ALL_REDS_INTERNAL(p, fcalls) \
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 58cd31cee9..872f0f9b2a 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -652,6 +652,8 @@ bif erts_debug:size_shared/1
bif erts_debug:copy_shared/1
bif erlang:has_prepared_code_on_load/1
+bif maps:take/2
+
#
# Obsolete
#
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 4f5e80f2e5..d02c6828f9 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -347,8 +347,11 @@ print_process_info(int to, void *to_arg, Process *p)
static void
print_garb_info(int to, void *to_arg, Process* p)
{
+#ifdef ERTS_SMP
/* ERTS_SMP: A scheduler is probably concurrently doing gc... */
-#ifndef ERTS_SMP
+ if (!ERTS_IS_CRASH_DUMPING)
+ return;
+#endif
erts_print(to, to_arg, "New heap start: %bpX\n", p->heap);
erts_print(to, to_arg, "New heap top: %bpX\n", p->htop);
erts_print(to, to_arg, "Stack top: %bpX\n", p->stop);
@@ -356,7 +359,6 @@ print_garb_info(int to, void *to_arg, Process* p)
erts_print(to, to_arg, "Old heap start: %bpX\n", OLD_HEAP(p));
erts_print(to, to_arg, "Old heap top: %bpX\n", OLD_HTOP(p));
erts_print(to, to_arg, "Old heap end: %bpX\n", OLD_HEND(p));
-#endif
}
void
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 52fd57e101..09c83f1117 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -397,7 +397,7 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
msgp = erts_alloc_message_heap(rp, &rp_locks,
3, &hp, &ohp);
tup = TUPLE2(hp, am_nodedown, name);
- erts_queue_message(rp, &rp_locks, msgp, tup);
+ erts_queue_message(rp, rp_locks, msgp, tup, am_system);
}
erts_smp_proc_unlock(rp, rp_locks);
}
@@ -1456,7 +1456,7 @@ int erts_net_message(Port *prt,
token = copy_struct(token, token_size, &hp, ohp);
}
- erts_queue_dist_message(rp, &locks, ede_copy, token);
+ erts_queue_dist_message(rp, locks, ede_copy, token, from);
if (locks)
erts_smp_proc_unlock(rp, locks);
}
@@ -1505,7 +1505,7 @@ int erts_net_message(Port *prt,
token = copy_struct(token, token_size, &hp, ohp);
}
- erts_queue_dist_message(rp, &locks, ede_copy, token);
+ erts_queue_dist_message(rp, locks, ede_copy, token, tuple[2]);
if (locks)
erts_smp_proc_unlock(rp, locks);
}
@@ -1967,7 +1967,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if (size > (Uint) INT_MAX)
- erts_exit(ERTS_ABORT_EXIT,
+ erts_exit(ERTS_DUMP_EXIT,
"Absurdly large distribution output data buffer "
"(%beu bytes) passed.\n",
size);
@@ -2007,7 +2007,7 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if (size > (Uint) INT_MAX)
- erts_exit(ERTS_ABORT_EXIT,
+ erts_exit(ERTS_DUMP_EXIT,
"Absurdly large distribution output data buffer "
"(%beu bytes) passed.\n",
size);
@@ -3317,7 +3317,7 @@ send_nodes_mon_msg(Process *rp,
}
ASSERT(hend == hp);
- erts_queue_message(rp, rp_locksp, mp, msg);
+ erts_queue_message(rp, *rp_locksp, mp, msg, am_system);
}
static void
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index d04977b9ae..c367d4162c 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -137,6 +137,11 @@ static ErtsAllocatorState_t exec_alloc_state;
#endif
static ErtsAllocatorState_t test_alloc_state;
+#define ERTS_ALC_INFO_A_ALLOC_UTIL (ERTS_ALC_A_MAX + 1)
+#define ERTS_ALC_INFO_A_MSEG_ALLOC (ERTS_ALC_A_MAX + 2)
+#define ERTS_ALC_INFO_A_ERTS_MMAP (ERTS_ALC_A_MAX + 3)
+#define ERTS_ALC_INFO_A_MAX ERTS_ALC_INFO_A_ERTS_MMAP
+
typedef struct {
erts_smp_atomic32_t refc;
int only_sz;
@@ -145,13 +150,9 @@ typedef struct {
Process *proc;
Eterm ref;
Eterm ref_heap[REF_THING_SIZE];
- int allocs[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+1+2];
+ int allocs[ERTS_ALC_INFO_A_MAX - ERTS_ALC_A_MIN + 1 + 1];
} ErtsAllocInfoReq;
-#define ERTS_ALC_INFO_A_ALLOC_UTIL (ERTS_ALC_A_MAX + 1)
-#define ERTS_ALC_INFO_A_MSEG_ALLOC (ERTS_ALC_A_MAX + 2)
-#define ERTS_ALC_INFO_A_MAX ERTS_ALC_INFO_A_MSEG_ALLOC
-
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(aireq,
ErtsAllocInfoReq,
5,
@@ -2840,10 +2841,18 @@ erts_allocator_info(int to, void *arg)
int i;
for (i = 0; i <= max; i++) {
erts_print(to, arg, "=allocator:mseg_alloc[%d]\n", i);
- erts_mseg_info(i, &to, arg, 0, NULL, NULL);
+ erts_mseg_info(i, &to, arg, 0, 0, NULL, NULL);
}
- erts_print(to, arg, "=allocator:mseg_alloc.erts_mmap\n");
+ erts_print(to, arg, "=allocator:erts_mmap.default_mmap\n");
erts_mmap_info(&erts_dflt_mmapper, &to, arg, NULL, NULL, &emis);
+#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ erts_print(to, arg, "=allocator:erts_mmap.literal_mmap\n");
+ erts_mmap_info(&erts_literal_mmapper, &to, arg, NULL, NULL, &emis);
+#endif
+#ifdef ERTS_ALC_A_EXEC
+ erts_print(to, arg, "=allocator:erts_mmap.exec_mmap\n");
+ erts_mmap_info(&erts_exec_mmapper, &to, arg, NULL, NULL, &emis);
+#endif
}
#endif
@@ -2954,6 +2963,11 @@ erts_allocator_options(void *proc)
atoms[length] = am_atom_put("alloc_util", 10);
terms[length++] = erts_alcu_au_info_options(NULL, NULL, hpp, szp);
+#if HAVE_ERTS_MMAP
+ atoms[length] = ERTS_MAKE_AM("erts_mmap");
+ terms[length++] = erts_mmap_info_options(&erts_dflt_mmapper, NULL, NULL,
+ NULL, hpp, szp);
+#endif
{
Eterm o[3], v[3];
o[0] = am_atom_put("m", 1);
@@ -2990,7 +3004,12 @@ erts_allocator_options(void *proc)
#if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
terms[length++] = am_atom_put("sys_aligned_alloc", 17);
#endif
-
+#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ terms[length++] = ERTS_MAKE_AM("literal_mmap");
+#endif
+#ifdef ERTS_ALC_A_EXEC
+ terms[length++] = ERTS_MAKE_AM("exec_mmap");
+#endif
features = length ? erts_bld_list(hpp, szp, length, terms) : NIL;
#if defined(__GLIBC__)
@@ -3075,7 +3094,15 @@ reply_alloc_info(void *vair)
Uint sz, *szp;
ErlOffHeap *ohp = NULL;
ErtsMessage *mp = NULL;
- struct erts_mmap_info_struct emis;
+#if HAVE_ERTS_MMAP
+ struct erts_mmap_info_struct mmap_info_dflt;
+# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ struct erts_mmap_info_struct mmap_info_literal;
+# endif
+# ifdef ERTS_ALC_A_EXEC
+ struct erts_mmap_info_struct mmap_info_exec;
+# endif
+#endif
int i;
Eterm (*info_func)(Allctr_t *,
int,
@@ -3181,26 +3208,53 @@ reply_alloc_info(void *vair)
make_small(0),
ainfo);
break;
+ case ERTS_ALC_INFO_A_ERTS_MMAP:
+ alloc_atom = erts_bld_atom(hpp, szp, "erts_mmap");
+#if HAVE_ERTS_MMAP
+ ainfo = (air->only_sz ? NIL :
+ erts_mmap_info(&erts_dflt_mmapper, NULL, NULL,
+ hpp, szp, &mmap_info_dflt));
+ ainfo = erts_bld_tuple3(hpp, szp,
+ alloc_atom,
+ erts_bld_atom(hpp,szp,"default_mmap"),
+ ainfo);
+# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
+ ai_list = erts_bld_cons(hpp, szp,
+ ainfo, ai_list);
+ ainfo = (air->only_sz ? NIL :
+ erts_mmap_info(&erts_literal_mmapper, NULL, NULL,
+ hpp, szp, &mmap_info_literal));
+ ainfo = erts_bld_tuple3(hpp, szp,
+ alloc_atom,
+ erts_bld_atom(hpp,szp,"literal_mmap"),
+ ainfo);
+# endif
+# ifdef ERTS_ALC_A_EXEC
+ ai_list = erts_bld_cons(hpp, szp,
+ ainfo, ai_list);
+ ainfo = (air->only_sz ? NIL :
+ erts_mmap_info(&erts_exec_mmapper, NULL, NULL,
+ hpp, szp, &mmap_info_exec));
+ ainfo = erts_bld_tuple3(hpp, szp,
+ alloc_atom,
+ erts_bld_atom(hpp,szp,"exec_mmap"),
+ ainfo);
+# endif
+#else /* !HAVE_ERTS_MMAP */
+ ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
+ am_false);
+#endif
+ break;
case ERTS_ALC_INFO_A_MSEG_ALLOC:
alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc");
#if HAVE_ERTS_MSEG
- ainfo = (air->only_sz
- ? NIL
- : erts_mseg_info(0, NULL, NULL, hpp != NULL,
- hpp, szp));
+ ainfo = erts_mseg_info(0, NULL, NULL, hpp != NULL,
+ air->only_sz, hpp, szp);
ainfo = erts_bld_tuple3(hpp, szp,
alloc_atom,
make_small(0),
ainfo);
- ai_list = erts_bld_cons(hpp, szp,
- ainfo, ai_list);
- ainfo = (air->only_sz ? NIL :
- erts_mmap_info(&erts_dflt_mmapper, NULL, NULL, hpp, szp, &emis));
- ainfo = erts_bld_tuple3(hpp, szp,
- alloc_atom,
- erts_bld_atom(hpp,szp,"erts_mmap"),
- ainfo);
#else
ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
am_false);
@@ -3232,15 +3286,14 @@ reply_alloc_info(void *vair)
}
switch (ai) {
case ERTS_ALC_A_SYSTEM:
- case ERTS_ALC_INFO_A_ALLOC_UTIL:
+ case ERTS_ALC_INFO_A_ALLOC_UTIL:
+ case ERTS_ALC_INFO_A_ERTS_MMAP:
break;
case ERTS_ALC_INFO_A_MSEG_ALLOC:
#if HAVE_ERTS_MSEG && defined(ERTS_SMP)
alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc");
- ainfo = (air->only_sz
- ? NIL
- : erts_mseg_info(sched_id, NULL, NULL,
- hpp != NULL, hpp, szp));
+ ainfo = erts_mseg_info(sched_id, NULL, NULL,
+ hpp != NULL, air->only_sz, hpp, szp);
ainfo = erts_bld_tuple(hpp, szp, 3,
alloc_atom,
make_small(sched_id),
@@ -3286,7 +3339,7 @@ reply_alloc_info(void *vair)
if (hp != hp_end)
erts_shrink_message_heap(&mp, rp, hp_start, hp, hp_end, &msg, 1);
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (air->req_sched == sched_id)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -3306,7 +3359,7 @@ erts_request_alloc_info(struct process *c_p,
int internal)
{
ErtsAllocInfoReq *air = aireq_alloc();
- Eterm req_ai[ERTS_ALC_A_MAX+1+2] = {0};
+ Eterm req_ai[ERTS_ALC_INFO_A_MAX+1] = {0};
Eterm alist;
Eterm *hp;
int airix = 0, ai;
@@ -3342,6 +3395,10 @@ erts_request_alloc_info(struct process *c_p,
ai = ERTS_ALC_INFO_A_MSEG_ALLOC;
goto save_alloc;
}
+ if (erts_is_atom_str("erts_mmap", alloc, 0)) {
+ ai = ERTS_ALC_INFO_A_ERTS_MMAP;
+ goto save_alloc;
+ }
if (erts_is_atom_str("alloc_util", alloc, 0)) {
ai = ERTS_ALC_INFO_A_ALLOC_UTIL;
save_alloc:
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index 4d6d699d9f..84254af0c2 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -283,10 +283,10 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
erts_thr_q_enqueue(&q->thr_q, a);
#ifdef USE_LTTNG_VM_TRACEPOINTS
- if (LTTNG_ENABLED(aio_pool_add)) {
+ if (LTTNG_ENABLED(aio_pool_put)) {
lttng_decl_portbuf(port_str);
lttng_portid_to_str(a->port, port_str);
- LTTNG2(aio_pool_add, port_str, -1);
+ LTTNG2(aio_pool_put, port_str, -1);
}
#endif
#ifdef USE_VM_PROBES
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index 1e2db38442..ef77201544 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1737,7 +1737,7 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type,
hp += REF_THING_SIZE;
mess = TUPLE5(hp,type,r,am_driver,driver_name,tag);
}
- erts_queue_message(proc, &rp_locks, mp, mess);
+ erts_queue_message(proc, rp_locks, mp, mess, am_system);
erts_smp_proc_unlock(proc, rp_locks);
ERTS_SMP_CHK_NO_PROC_LOCKS;
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index c9741b361f..da480f8fce 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -45,6 +45,7 @@
#include "erl_async.h"
#include "erl_thr_progress.h"
#include "erl_bif_unique.h"
+#include "erl_map.h"
#define ERTS_PTAB_WANT_DEBUG_FUNCS__
#include "erl_ptab.h"
#ifdef HIPE
@@ -594,6 +595,7 @@ static Eterm pi_args[] = {
am_suspending,
am_min_heap_size,
am_min_bin_vheap_size,
+ am_max_heap_size,
am_current_location,
am_current_stacktrace,
am_message_queue_data,
@@ -643,10 +645,11 @@ pi_arg2ix(Eterm arg)
case am_suspending: return 26;
case am_min_heap_size: return 27;
case am_min_bin_vheap_size: return 28;
- case am_current_location: return 29;
- case am_current_stacktrace: return 30;
- case am_message_queue_data: return 31;
- case am_garbage_collection_info: return 32;
+ case am_max_heap_size: return 29;
+ case am_current_location: return 30;
+ case am_current_stacktrace: return 31;
+ case am_message_queue_data: return 32;
+ case am_garbage_collection_info: return 33;
default: return -1;
}
}
@@ -1348,6 +1351,18 @@ process_info_aux(Process *BIF_P,
break;
}
+ case am_max_heap_size: {
+ Uint hsz = 3;
+ (void) erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp),
+ MAX_HEAP_SIZE_FLAGS_GET(rp),
+ NULL, &hsz);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp),
+ MAX_HEAP_SIZE_FLAGS_GET(rp),
+ &hp, NULL);
+ break;
+ }
+
case am_total_heap_size: {
ErtsMessage *mp;
Uint total_heap_size;
@@ -1359,9 +1374,10 @@ process_info_aux(Process *BIF_P,
total_heap_size += rp->mbuf_sz;
- for (mp = rp->msg.first; mp; mp = mp->next)
- if (mp->data.attached)
- total_heap_size += erts_msg_attached_data_size(mp);
+ if (rp->flags & F_ON_HEAP_MSGQ)
+ for (mp = rp->msg.first; mp; mp = mp->next)
+ if (mp->data.attached)
+ total_heap_size += erts_msg_attached_data_size(mp);
(void) erts_bld_uint(NULL, &hsz, total_heap_size);
hp = HAlloc(BIF_P, hsz);
@@ -1390,8 +1406,12 @@ process_info_aux(Process *BIF_P,
case am_garbage_collection: {
DECL_AM(minor_gcs);
Eterm t;
+ Uint map_sz = 0;
+
+ erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), MAX_HEAP_SIZE_FLAGS_GET(rp), NULL, &map_sz);
- hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2 + 3); /* last "3" is for outside tuple */
+ hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + map_sz + 3);
+ /* last "3" is for outside tuple */
t = TUPLE2(hp, AM_minor_gcs, make_small(GEN_GCS(rp))); hp += 3;
res = CONS(hp, t, NIL); hp += 2;
@@ -1402,6 +1422,11 @@ process_info_aux(Process *BIF_P,
res = CONS(hp, t, res); hp += 2;
t = TUPLE2(hp, am_min_bin_vheap_size, make_small(MIN_VHEAP_SIZE(rp))); hp += 3;
res = CONS(hp, t, res); hp += 2;
+
+ t = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), MAX_HEAP_SIZE_FLAGS_GET(rp), &hp, NULL);
+
+ t = TUPLE2(hp, am_max_heap_size, t); hp += 3;
+ res = CONS(hp, t, res); hp += 2;
break;
}
@@ -1411,12 +1436,12 @@ process_info_aux(Process *BIF_P,
if (rp == BIF_P) {
sz += ERTS_PROCESS_GC_INFO_MAX_SIZE;
} else {
- erts_process_gc_info(rp, &sz, NULL);
+ erts_process_gc_info(rp, &sz, NULL, 0, 0);
sz += 3;
}
hp = HAlloc(BIF_P, sz);
- res = erts_process_gc_info(rp, &actual_sz, &hp);
+ res = erts_process_gc_info(rp, &actual_sz, &hp, 0, 0);
/* We may have some extra space, fill with 0 tuples */
if (actual_sz <= sz - 3) {
@@ -2172,7 +2197,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (BIF_ARG_1 == am_garbage_collection){
Uint val = (Uint) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
Eterm tup;
- hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2);
+ hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2);
tup = TUPLE2(hp, am_fullsweep_after, make_small(val)); hp += 3;
res = CONS(hp, tup, NIL); hp += 2;
@@ -2183,6 +2208,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
tup = TUPLE2(hp, am_min_bin_vheap_size, make_small(BIN_VH_MIN_SIZE)); hp += 3;
res = CONS(hp, tup, res); hp += 2;
+ tup = TUPLE2(hp, am_max_heap_size, make_small(H_MAX_SIZE)); hp += 3;
+ res = CONS(hp, tup, res); hp += 2;
+
BIF_RET(res);
} else if (BIF_ARG_1 == am_fullsweep_after){
Uint val = (Uint) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
@@ -2193,6 +2221,12 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_min_heap_size,make_small(H_MIN_SIZE));
BIF_RET(res);
+ } else if (BIF_ARG_1 == am_max_heap_size) {
+ Uint sz = 0;
+ erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, NULL, &sz);
+ hp = HAlloc(BIF_P, sz);
+ res = erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, &hp, NULL);
+ BIF_RET(res);
} else if (BIF_ARG_1 == am_min_bin_vheap_size) {
hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_min_bin_vheap_size,make_small(BIN_VH_MIN_SIZE));
@@ -3554,7 +3588,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(res);
}
else if (ERTS_IS_ATOM_STR("mmap", BIF_ARG_1)) {
- BIF_RET(erts_mmap_debug_info(&erts_dflt_mmapper, BIF_P));
+ BIF_RET(erts_mmap_debug_info(BIF_P));
}
else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) {
BIF_RET(erts_debug_get_unique_monotonic_integer_state(BIF_P));
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index ff2018aa27..b65c0e303f 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -68,6 +68,9 @@ static struct { /* Protected by code write permission */
static Eterm
trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist);
+static int
+erts_set_tracing_event_pattern(Eterm event, Binary*, int on);
+
#ifdef ERTS_SMP
static void smp_bp_finisher(void* arg);
#endif
@@ -78,6 +81,8 @@ static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/
static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
+static Eterm trace_info_event(Process* p, Eterm event, Eterm key);
+
static void reset_bif_trace(void);
static void setup_bif_trace(void);
@@ -85,14 +90,26 @@ static void install_exp_breakpoints(BpFunctions* f);
static void uninstall_exp_breakpoints(BpFunctions* f);
static void clean_export_entries(BpFunctions* f);
+ErtsTracingEvent erts_send_tracing[ERTS_NUM_BP_IX];
+ErtsTracingEvent erts_receive_tracing[ERTS_NUM_BP_IX];
+
void
erts_bif_trace_init(void)
{
+ int i;
+
erts_default_trace_pattern_is_on = 0;
erts_default_match_spec = NULL;
erts_default_meta_match_spec = NULL;
erts_default_trace_pattern_flags = erts_trace_pattern_flags_off;
erts_default_meta_tracer = erts_tracer_nil;
+
+ for (i=0; i<ERTS_NUM_BP_IX; i++) {
+ erts_send_tracing[i].on = 1;
+ erts_send_tracing[i].match_spec = NULL;
+ erts_receive_tracing[i].on = 1;
+ erts_receive_tracing[i].match_spec = NULL;
+ }
}
/*
@@ -137,15 +154,18 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
on = 1;
} else if (Pattern == am_restart) {
match_prog_set = NULL;
- on = erts_break_reset;
+ on = ERTS_BREAK_RESTART;
} else if (Pattern == am_pause) {
match_prog_set = NULL;
- on = erts_break_stop;
- } else if ((match_prog_set = erts_match_set_compile(p, Pattern)) != NULL) {
- MatchSetRef(match_prog_set);
- on = 1;
- } else{
- goto error;
+ on = ERTS_BREAK_PAUSE;
+ } else {
+ match_prog_set = erts_match_set_compile(p, Pattern, MFA);
+ if (match_prog_set) {
+ MatchSetRef(match_prog_set);
+ on = 1;
+ } else{
+ goto error;
+ }
}
is_global = 0;
@@ -314,6 +334,11 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
matches = erts_set_trace_pattern(p, mfa, specified,
match_prog_set, match_prog_set,
on, flags, meta_tracer, 0);
+ } else if (is_atom(MFA)) {
+ if (is_global || flags.breakpoint || on > ERTS_BREAK_SET) {
+ goto error;
+ }
+ matches = erts_set_tracing_event_pattern(MFA, match_prog_set, on);
}
error:
@@ -791,6 +816,8 @@ Eterm trace_info_2(BIF_ALIST_2)
if (What == am_on_load) {
res = trace_info_on_load(p, Key);
+ } else if (What == am_send || What == am_receive) {
+ res = trace_info_event(p, What, Key);
} else if (is_atom(What) || is_pid(What) || is_port(What)) {
res = trace_info_pid(p, What, Key);
} else if (is_tuple(What)) {
@@ -1280,6 +1307,42 @@ trace_info_on_load(Process* p, Eterm key)
}
}
+static Eterm
+trace_info_event(Process* p, Eterm event, Eterm key)
+{
+ ErtsTracingEvent* te;
+ Eterm retval;
+ Eterm* hp;
+
+ switch (event) {
+ case am_send: te = erts_send_tracing; break;
+ case am_receive: te = erts_receive_tracing; break;
+ default:
+ goto error;
+ }
+
+ if (key != am_match_spec)
+ goto error;
+
+ te = &te[erts_active_bp_ix()];
+
+ if (te->on) {
+ if (!te->match_spec)
+ retval = am_true;
+ else
+ retval = copy_object(MatchSetGetSource(te->match_spec), p);
+ }
+ else
+ retval = am_false;
+
+ hp = HAlloc(p, 3);
+ return TUPLE2(hp, key, retval);
+
+ error:
+ BIF_ERROR(p, BADARG);
+}
+
+
#undef FUNC_TRACE_NOEXIST
#undef FUNC_TRACE_UNTRACED
#undef FUNC_TRACE_GLOBAL_TRACE
@@ -1307,7 +1370,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
for (i = 0; i < n; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code));
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (on && !flags.breakpoint) {
/* Turn on global call tracing */
@@ -1468,12 +1531,57 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
}
int
+erts_set_tracing_event_pattern(Eterm event, Binary* match_spec, int on)
+{
+ ErtsBpIndex ix = erts_staging_bp_ix();
+ ErtsTracingEvent* st;
+
+ switch (event) {
+ case am_send: st = &erts_send_tracing[ix]; break;
+ case am_receive: st = &erts_receive_tracing[ix]; break;
+ default: return -1;
+ }
+
+ MatchSetUnref(st->match_spec);
+
+ st->on = on;
+ st->match_spec = match_spec;
+ MatchSetRef(match_spec);
+
+ finish_bp.current = 1; /* prepare phase not needed for event trace */
+ finish_bp.install = on;
+ finish_bp.e.matched = 0;
+ finish_bp.e.matching = NULL;
+ finish_bp.f.matched = 0;
+ finish_bp.f.matching = NULL;
+
+#ifndef ERTS_SMP
+ while (erts_finish_breakpointing()) {
+ /* Empty loop body */
+ }
+#endif
+ return 1;
+}
+
+static void
+consolidate_event_tracing(ErtsTracingEvent te[])
+{
+ ErtsTracingEvent* src = &te[erts_active_bp_ix()];
+ ErtsTracingEvent* dst = &te[erts_staging_bp_ix()];
+
+ MatchSetUnref(dst->match_spec);
+ dst->on = src->on;
+ dst->match_spec = src->match_spec;
+ MatchSetRef(dst->match_spec);
+}
+
+int
erts_finish_breakpointing(void)
{
ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
/*
- * Memory barriers will be issued for all processes *before*
+ * Memory barriers will be issued for all schedulers *before*
* each of the stages below. (Unless the other schedulers
* are blocked, in which case memory barriers will be issued
* when they are awaken.)
@@ -1542,6 +1650,8 @@ erts_finish_breakpointing(void)
erts_consolidate_bp_data(&finish_bp.f, 1);
erts_bp_free_matched_functions(&finish_bp.e);
erts_bp_free_matched_functions(&finish_bp.f);
+ consolidate_event_tracing(erts_send_tracing);
+ consolidate_event_tracing(erts_receive_tracing);
return 0;
default:
ASSERT(0);
@@ -1556,11 +1666,10 @@ install_exp_breakpoints(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
ep->addressv[code_ix] = pc;
}
@@ -1573,11 +1682,10 @@ uninstall_exp_breakpoints(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (ep->addressv[code_ix] != pc) {
continue;
@@ -1594,11 +1702,10 @@ clean_export_entries(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (ep->addressv[code_ix] == pc) {
continue;
@@ -2277,7 +2384,7 @@ reply_trace_delivered_all(void *vtdarp)
#ifdef ERTS_SMP
erts_send_sys_msg_proc(rp->common.id, rp->common.id, msg, bp);
#else
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
#endif
erts_free(ERTS_ALC_T_MISC_AUX_WORK, vtdarp);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 615d23402b..acaca54e9a 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -2833,7 +2833,8 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3)
BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3],
BIF_P,lst,BIF_ARG_2,ret);
}
- res = db_prog_match(BIF_P, mp, CAR(list_val(lst)), NULL, 0,
+ res = db_prog_match(BIF_P, BIF_P,
+ mp, CAR(list_val(lst)), NULL, 0,
ERTS_PAM_COPY_RESULT, &dummy);
if (is_value(res)) {
hp = HAlloc(BIF_P, 2);
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 76b96637ae..95b1cd0148 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -123,6 +123,9 @@ do { \
#define TermWords(t) (((t) / (sizeof(UWord)/sizeof(Eterm))) + !!((t) % (sizeof(UWord)/sizeof(Eterm))))
+#define add_dmc_err(EINFO, STR, VAR, TERM, SEV) \
+ vadd_dmc_err(EINFO, SEV, VAR, STR, TERM)
+
static ERTS_INLINE Process *
get_proc(Process *cp, Uint32 cp_locks, Eterm id, Uint32 id_locks)
@@ -411,17 +414,27 @@ get_match_pseudo_process(Process *c_p, Uint heap_size)
{
ErtsMatchPseudoProcess *mpsp;
#ifdef ERTS_SMP
- mpsp = (ErtsMatchPseudoProcess *) c_p->scheduler_data->match_pseudo_process;
- if (mpsp)
+ ErtsSchedulerData *esdp;
+
+ esdp = c_p ? c_p->scheduler_data : erts_get_scheduler_data();
+
+ mpsp = esdp ? esdp->match_pseudo_process :
+ (ErtsMatchPseudoProcess*) erts_smp_tsd_get(match_pseudo_process_key);
+
+ if (mpsp) {
+ ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key));
+ ASSERT(mpsp->process.scheduler_data == esdp);
cleanup_match_pseudo_process(mpsp, 0);
+ }
else {
ASSERT(erts_smp_tsd_get(match_pseudo_process_key) == NULL);
mpsp = create_match_pseudo_process();
- c_p->scheduler_data->match_pseudo_process = (void *) mpsp;
+ if (esdp) {
+ esdp->match_pseudo_process = (void *) mpsp;
+ }
+ mpsp->process.scheduler_data = esdp;
erts_smp_tsd_set(match_pseudo_process_key, (void *) mpsp);
}
- ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key));
- mpsp->process.scheduler_data = c_p->scheduler_data;
#else
mpsp = match_pseudo_process;
cleanup_match_pseudo_process(mpsp, 0);
@@ -889,11 +902,7 @@ void db_match_dis(Binary *prog);
#define TRACE /* Nothing */
#define FENCE_PATTERN_SIZE 0
#endif
-static void add_dmc_err(DMCErrInfo *err_info,
- char *str,
- int variable,
- Eterm term,
- DMCErrorSeverity severity);
+static void vadd_dmc_err(DMCErrInfo*, DMCErrorSeverity, int var, const char *str, ...);
static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity);
@@ -989,12 +998,20 @@ Eterm erts_match_set_get_source(Binary *mpsp)
}
/* This one is for the tracing */
-Binary *erts_match_set_compile(Process *p, Eterm matchexpr) {
+Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA) {
Binary *bin;
Uint sz;
Eterm *hp;
+ Uint flags;
+
+ switch (MFA) {
+ case am_receive: flags = DCOMP_TRACE; break;
+ case am_send: flags = DCOMP_TRACE | DCOMP_ALLOW_TRACE_OPS; break;
+ default:
+ flags = DCOMP_TRACE | DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS;
+ }
- bin = db_match_set_compile(p, matchexpr, DCOMP_TRACE);
+ bin = db_match_set_compile(p, matchexpr, flags);
if (bin != NULL) {
MatchProg *prog = Binary2MatchProg(bin);
sz = size_object(matchexpr);
@@ -1124,8 +1141,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags)
int i;
if (!is_list(matchexpr)) {
- add_dmc_err(err_info, "Match programs are not in a list.",
- -1, 0UL, dmcError);
+ add_dmc_err(err_info, "Match programs are not in a list.",
+ -1, 0UL, dmcError);
goto done;
}
num_heads = 0;
@@ -1133,9 +1150,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags)
++num_heads;
if (l != NIL) { /* proper list... */
- add_dmc_err(err_info, "Match programs are not in a proper "
- "list.",
- -1, 0UL, dmcError);
+ add_dmc_err(err_info, "Match programs are not in a proper list.",
+ -1, 0UL, dmcError);
goto done;
}
@@ -1202,30 +1218,37 @@ done:
return ret;
}
-Eterm erts_match_set_run(Process *p, Binary *mpsp,
- Eterm *args, int num_args,
- enum erts_pam_run_flags in_flags,
- Uint32 *return_flags)
+/* Returns
+ * am_false if no match or
+ * if {message,false} has been called,
+ * am_true if {message,_} has NOT been called or
+ * if {message,true} has been called,
+ * Msg if {message,Msg} has been called.
+ *
+ * If return value is_not_immed
+ * then erts_match_set_release_result_trace() must be called to release it.
+ */
+Eterm erts_match_set_run_trace(Process *c_p,
+ Process *self,
+ Binary *mpsp,
+ Eterm *args, int num_args,
+ enum erts_pam_run_flags in_flags,
+ Uint32 *return_flags)
{
Eterm ret;
- ret = db_prog_match(p, mpsp, NIL, args, num_args,
+ ret = db_prog_match(c_p, self, mpsp, NIL, args, num_args,
in_flags, return_flags);
-#if defined(HARDDEBUG)
- if (is_non_value(ret)) {
- erts_fprintf(stderr, "Failed\n");
- } else {
- erts_fprintf(stderr, "Returning : %T\n", ret);
+
+ ASSERT(!(is_non_value(ret) && *return_flags));
+
+ if (is_non_value(ret) || ret == am_false) {
+ erts_match_set_release_result(c_p);
+ return am_false;
}
-#endif
+ if (is_immed(ret))
+ erts_match_set_release_result(c_p);
return ret;
- /* Returns
- * THE_NON_VALUE if no match
- * am_false if {message,false} has been called,
- * am_true if {message,_} has not been called or
- * if {message,true} has been called,
- * Msg if {message,Msg} has been called.
- */
}
static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
@@ -1234,7 +1257,8 @@ static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
{
Eterm ret;
- ret = db_prog_match(p, mpsp, args, NULL, num_args,
+ ret = db_prog_match(p, p,
+ mpsp, args, NULL, num_args,
ERTS_PAM_COPY_RESULT,
return_flags);
#if defined(HARDDEBUG)
@@ -1730,7 +1754,9 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity)
** the parameter 'arity' is only used if 'term' is actually an array,
** i.e. 'DCOMP_TRACE' was specified
*/
-Eterm db_prog_match(Process *c_p, Binary *bprog,
+Eterm db_prog_match(Process *c_p,
+ Process *self,
+ Binary *bprog,
Eterm term,
Eterm *termp,
int arity,
@@ -1743,10 +1769,10 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
Eterm *esp;
MatchVariable* variables;
BeamInstr *cp;
- UWord *pc = prog->text;
+ const UWord *pc = prog->text;
Eterm *ehp;
Eterm ret;
- Uint n = 0; /* To avoid warning. */
+ Uint n;
int i;
unsigned do_catch;
ErtsMatchPseudoProcess *mpsp;
@@ -1758,46 +1784,28 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
Eterm (*bif)(Process*, ...);
Eterm bif_args[3];
int fail_label;
- int atomic_trace;
#ifdef DMC_DEBUG
Uint *heap_fence;
Uint *stack_fence;
Uint save_op;
#endif /* DMC_DEBUG */
+ ERTS_UNDEF(n,0);
+ ERTS_UNDEF(current_scheduled,NULL);
+
+ ASSERT(c_p || !(in_flags & ERTS_PAM_COPY_RESULT));
+
mpsp = get_match_pseudo_process(c_p, prog->heap_size);
psp = &mpsp->process;
/* We need to lure the scheduler into believing in the pseudo process,
because of floating point exceptions. Do *after* mpsp is set!!! */
- esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(c_p);
- ASSERT(esdp != NULL);
- current_scheduled = esdp->current_process;
+ esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(psp);
+ if (esdp)
+ current_scheduled = esdp->current_process;
/* SMP: psp->scheduler_data is set by get_match_pseudo_process */
- atomic_trace = 0;
-#define BEGIN_ATOMIC_TRACE(p) \
- do { \
- if (! atomic_trace) { \
- erts_refc_inc(&bprog->refc, 2); \
- erts_smp_proc_unlock((p), ERTS_PROC_LOCK_MAIN); \
- erts_smp_thr_progress_block(); \
- atomic_trace = !0; \
- } \
- } while (0)
-#define END_ATOMIC_TRACE(p) \
- do { \
- if (atomic_trace) { \
- erts_smp_thr_progress_unblock(); \
- erts_smp_proc_lock((p), ERTS_PROC_LOCK_MAIN); \
- if (erts_refc_dectest(&bprog->refc, 0) == 0) {\
- erts_bin_free(bprog); \
- } \
- atomic_trace = 0; \
- } \
- } while (0)
-
#ifdef DMC_DEBUG
save_op = 0;
heap_fence = (Eterm*)((char*) mpsp->u.heap + prog->stack_offset) - 1;
@@ -2256,7 +2264,7 @@ restart:
pc += n;
break;
case matchSelf:
- *esp++ = c_p->common.id;
+ *esp++ = self->common.id;
break;
case matchWaste:
--esp;
@@ -2266,6 +2274,7 @@ restart:
break;
case matchProcessDump: {
erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
+ ASSERT(c_p == self);
print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p);
*esp++ = new_binary(build_proc, (byte *)dsbufp->str,
dsbufp->str_len);
@@ -2284,14 +2293,16 @@ restart:
*return_flags |= MATCH_SET_EXCEPTION_TRACE;
*esp++ = am_true;
break;
- case matchIsSeqTrace:
+ case matchIsSeqTrace:
+ ASSERT(c_p == self);
if (have_seqtrace(SEQ_TRACE_TOKEN(c_p)))
*esp++ = am_true;
else
*esp++ = am_false;
break;
case matchSetSeqToken:
- t = erts_seq_trace(c_p, esp[-1], esp[-2], 0);
+ ASSERT(c_p == self);
+ t = erts_seq_trace(c_p, esp[-1], esp[-2], 0);
if (is_non_value(t)) {
esp[-2] = FAIL_TERM;
} else {
@@ -2299,7 +2310,8 @@ restart:
}
--esp;
break;
- case matchSetSeqTokenFake:
+ case matchSetSeqTokenFake:
+ ASSERT(c_p == self);
t = seq_trace_fake(c_p, esp[-1]);
if (is_non_value(t)) {
esp[-2] = FAIL_TERM;
@@ -2308,7 +2320,8 @@ restart:
}
--esp;
break;
- case matchGetSeqToken:
+ case matchGetSeqToken:
+ ASSERT(c_p == self);
if (have_no_seqtrace(SEQ_TRACE_TOKEN(c_p)))
*esp++ = NIL;
else {
@@ -2332,49 +2345,62 @@ restart:
ASSERT(is_immed(ehp[5]));
}
break;
- case matchEnableTrace:
+ case matchEnableTrace:
+ ASSERT(c_p == self);
if ( (n = erts_trace_flag2bit(esp[-1]))) {
- BEGIN_ATOMIC_TRACE(c_p);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
set_tracee_flags(c_p, ERTS_TRACER(c_p), 0, n);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
}
break;
- case matchEnableTrace2:
+ case matchEnableTrace2:
+ ASSERT(c_p == self);
n = erts_trace_flag2bit((--esp)[-1]);
esp[-1] = FAIL_TERM;
if (n) {
- BEGIN_ATOMIC_TRACE(c_p);
- if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
+ if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) {
/* Always take over the tracer of the current process */
set_tracee_flags(tmpp, ERTS_TRACER(c_p), 0, n);
- esp[-1] = am_true;
+ if (tmpp == c_p)
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR);
+ else
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL);
+ esp[-1] = am_true;
}
}
break;
- case matchDisableTrace:
+ case matchDisableTrace:
+ ASSERT(c_p == self);
if ( (n = erts_trace_flag2bit(esp[-1]))) {
- BEGIN_ATOMIC_TRACE(c_p);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
set_tracee_flags(c_p, ERTS_TRACER(c_p), n, 0);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
}
break;
- case matchDisableTrace2:
+ case matchDisableTrace2:
+ ASSERT(c_p == self);
n = erts_trace_flag2bit((--esp)[-1]);
esp[-1] = FAIL_TERM;
if (n) {
- BEGIN_ATOMIC_TRACE(c_p);
- if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
+ if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) {
/* Always take over the tracer of the current process */
set_tracee_flags(tmpp, ERTS_TRACER(c_p), n, 0);
- esp[-1] = am_true;
+ if (tmpp == c_p)
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR);
+ else
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL);
+ esp[-1] = am_true;
}
}
break;
- case matchCaller:
+ case matchCaller:
+ ASSERT(c_p == self);
if (!(c_p->cp) || !(cp = find_function_from_pc(c_p->cp))) {
*esp++ = am_undefined;
} else {
@@ -2386,7 +2412,8 @@ restart:
ehp[3] = make_small((Uint) cp[2]);
}
break;
- case matchSilent:
+ case matchSilent:
+ ASSERT(c_p == self);
--esp;
if (in_flags & ERTS_PAM_IGNORE_TRACE_SILENT)
break;
@@ -2401,7 +2428,8 @@ restart:
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
}
break;
- case matchTrace2:
+ case matchTrace2:
+ ASSERT(c_p == self);
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
@@ -2433,7 +2461,8 @@ restart:
ERTS_TRACER_CLEAR(&tracer);
}
break;
- case matchTrace3:
+ case matchTrace3:
+ ASSERT(c_p == self);
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
@@ -2506,15 +2535,12 @@ success:
}
#endif
- esdp->current_process = current_scheduled;
-
- END_ATOMIC_TRACE(c_p);
+ if (esdp)
+ esdp->current_process = current_scheduled;
return ret;
#undef FAIL
#undef FAIL_TERM
-#undef BEGIN_ATOMIC_TRACE
-#undef END_ATOMIC_TRACE
}
@@ -3259,20 +3285,20 @@ int erts_db_is_compiled_ms(Eterm term)
** Utility to add an error
*/
-static void add_dmc_err(DMCErrInfo *err_info,
- char *str,
- int variable,
- Eterm term,
- DMCErrorSeverity severity)
+static void vadd_dmc_err(DMCErrInfo *err_info,
+ DMCErrorSeverity severity,
+ int variable,
+ const char *str,
+ ...)
{
+ DMCError *e;
+ va_list args;
+ va_start(args, str);
+
+
/* Linked in in reverse order, to ease the formatting */
- DMCError *e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError));
- if (term != 0UL) {
- erts_snprintf(e->error_string, DMC_ERR_STR_LEN, str, term);
- } else {
- strncpy(e->error_string, str, DMC_ERR_STR_LEN);
- e->error_string[DMC_ERR_STR_LEN] ='\0';
- }
+ e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError));
+ erts_vsnprintf(e->error_string, DMC_ERR_STR_LEN, str, args);
e->variable = variable;
e->severity = severity;
e->next = err_info->first;
@@ -3282,8 +3308,11 @@ static void add_dmc_err(DMCErrInfo *err_info,
err_info->first = e;
if (severity >= dmcError)
err_info->error_added = 1;
+
+ va_end(args);
}
+
/*
** Handle one term in the match expression (not the guard)
*/
@@ -3482,24 +3511,21 @@ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text,
context->stack_need = context->stack_used;
}
-#define RETURN_ERROR_X(String, X, Y, ContextP, ConstantF) \
-do { \
-if ((ContextP)->err_info != NULL) { \
- (ConstantF) = 0; \
- add_dmc_err((ContextP)->err_info, String, X, Y, dmcError); \
- return retOk; \
-} else \
- return retFail; \
-} while(0)
+#define RETURN_ERROR_X(VAR, ContextP, ConstantF, String, ARG) \
+ (((ContextP)->err_info != NULL) \
+ ? ((ConstantF) = 0, \
+ vadd_dmc_err((ContextP)->err_info, dmcError, VAR, String, ARG), \
+ retOk) \
+ : retFail)
#define RETURN_ERROR(String, ContextP, ConstantF) \
- RETURN_ERROR_X(String, -1, 0UL, ContextP, ConstantF)
+ return RETURN_ERROR_X(-1, ContextP, ConstantF, String, 0)
#define RETURN_VAR_ERROR(String, N, ContextP, ConstantF) \
- RETURN_ERROR_X(String, N, 0UL, ContextP, ConstantF)
+ return RETURN_ERROR_X(N, ContextP, ConstantF, String, 0)
#define RETURN_TERM_ERROR(String, T, ContextP, ConstantF) \
- RETURN_ERROR_X(String, -1, T, ContextP, ConstantF)
+ return RETURN_ERROR_X(-1, ContextP, ConstantF, String, T)
#define WARNING(String, ContextP) \
add_dmc_err((ContextP)->err_info, String, -1, 0UL, dmcWarning)
@@ -3765,7 +3791,7 @@ static DMCRet dmc_variable(DMCContext *context,
Uint n = db_is_variable(t);
if (n >= heap->vars_used || !heap->vars[n].is_bound) {
- RETURN_VAR_ERROR("Variable $%d is unbound.", n, context, *constant);
+ RETURN_VAR_ERROR("Variable $%%d is unbound.", n, context, *constant);
}
dmc_add_pushv_variant(context, heap, text, n);
@@ -4097,7 +4123,30 @@ static DMCRet dmc_exception_trace(DMCContext *context,
return retOk;
}
-
+static int check_trace(const char* op,
+ DMCContext *context,
+ int *constant,
+ int need_cflags,
+ int allow_in_guard,
+ DMCRet* retp)
+{
+ if (!(context->cflags & DCOMP_TRACE)) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "used in wrong dialect.", op);
+ return 0;
+ }
+ if ((context->cflags & need_cflags) != need_cflags) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "not allow for this trace event.", op);
+ return 0;
+ }
+ if (context->is_guard && !allow_in_guard) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "called in guard context.", op);
+ return 0;
+ }
+ return 1;
+}
static DMCRet dmc_is_seq_trace(DMCContext *context,
DMCHeap *heap,
@@ -4107,12 +4156,11 @@ static DMCRet dmc_is_seq_trace(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'is_seq_trace' used in wrong dialect.",
- context,
- *constant);
- }
+ if (!check_trace("is_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 1, &ret))
+ return ret;
+
if (a != 1) {
RETURN_TERM_ERROR("Special form 'is_seq_trace' called with "
"arguments in %T.", t, context, *constant);
@@ -4136,16 +4184,8 @@ static DMCRet dmc_set_seq_token(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'set_seq_token' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'set_seq_token' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("set_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 3) {
RETURN_TERM_ERROR("Special form 'set_seq_token' called with wrong "
@@ -4182,16 +4222,11 @@ static DMCRet dmc_get_seq_token(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
+
+ if (!check_trace("get_seq_token", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'get_seq_token' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'get_seq_token' called in "
- "guard context.", context, *constant);
- }
if (a != 1) {
RETURN_TERM_ERROR("Special form 'get_seq_token' called with "
"arguments in %T.", t, context,
@@ -4255,16 +4290,10 @@ static DMCRet dmc_process_dump(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'process_dump' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'process_dump' called in "
- "guard context.", context, *constant);
- }
+ DMCRet ret;
+
+ if (!check_trace("process_dump", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 1) {
RETURN_TERM_ERROR("Special form 'process_dump' called with "
@@ -4288,17 +4317,8 @@ static DMCRet dmc_enable_trace(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'enable_trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'enable_trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("enable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 2:
@@ -4347,18 +4367,9 @@ static DMCRet dmc_disable_trace(DMCContext *context,
Uint a = arityval(*p);
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'disable_trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'disable_trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("disable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 2:
@@ -4408,17 +4419,8 @@ static DMCRet dmc_trace(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 3:
@@ -4479,16 +4481,11 @@ static DMCRet dmc_caller(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'caller' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'caller' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("caller", context, constant,
+ (DCOMP_CALL_TRACE|DCOMP_ALLOW_TRACE_OPS), 0, &ret))
+ return ret;
if (a != 1) {
RETURN_TERM_ERROR("Special form 'caller' called with "
@@ -4514,15 +4511,8 @@ static DMCRet dmc_silent(DMCContext *context,
DMCRet ret;
int c;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'silent' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'silent' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("silent", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 2) {
RETURN_TERM_ERROR("Special form 'silent' called with wrong "
@@ -5062,11 +5052,14 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
return THE_NON_VALUE;
}
if (trace) {
- lint_res = db_match_set_lint(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE);
- mps = db_match_set_compile(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE);
+ const Uint cflags = (DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE |
+ DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS);
+ lint_res = db_match_set_lint(p, spec, cflags);
+ mps = db_match_set_compile(p, spec, cflags);
} else {
- lint_res = db_match_set_lint(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
- mps = db_match_set_compile(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
+ const Uint cflags = (DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
+ lint_res = db_match_set_lint(p, spec, cflags);
+ mps = db_match_set_compile(p, spec, cflags);
}
if (mps == NULL) {
@@ -5099,7 +5092,8 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
}
save_cp = p->cp;
p->cp = NULL;
- res = erts_match_set_run(p, mps, arr, n,
+ res = erts_match_set_run_trace(p, p,
+ mps, arr, n,
ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT,
&ret_flags);
p->cp = save_cp;
@@ -5182,7 +5176,8 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog,
obj = db_alloc_tmp_uncompressed(tb, obj);
}
- res = db_prog_match(c_p, bprog, make_tuple(obj->tpl), NULL, 0,
+ res = db_prog_match(c_p, c_p,
+ bprog, make_tuple(obj->tpl), NULL, 0,
ERTS_PAM_COPY_RESULT|ERTS_PAM_CONTIGUOUS_TUPLE, &dummy);
if (is_value(res) && hpp!=NULL) {
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 5d7946a525..60f7067d70 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -425,6 +425,11 @@ typedef struct dmc_err_info {
#define DCOMP_FAKE_DESTRUCTIVE ((Uint) 8) /* When this is active, no setting of
trace control words or seq_trace tokens will be done. */
+/* Allow lock seizing operations on the tracee and 3rd party processes */
+#define DCOMP_ALLOW_TRACE_OPS ((Uint) 0x10)
+
+/* This is call trace */
+#define DCOMP_CALL_TRACE ((Uint) 0x20)
Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
Eterm *body, int num_matches,
@@ -435,7 +440,8 @@ Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog,
int all, DbTerm* obj, Eterm** hpp, Uint extra);
-Eterm db_prog_match(Process *p, Binary *prog, Eterm term,
+Eterm db_prog_match(Process *p, Process *self,
+ Binary *prog, Eterm term,
Eterm *termp, int arity,
enum erts_pam_run_flags in_flags,
Uint32 *return_flags /* Zeroed on enter */);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 4698458521..fa0782867d 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -41,6 +41,7 @@
#endif
#include "dtrace-wrapper.h"
#include "erl_bif_unique.h"
+#include "dist.h"
#define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1
#define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20
@@ -116,7 +117,7 @@ static Eterm *full_sweep_heaps(Process *p,
char *oh, Uint oh_size,
Eterm *objv, int nobj);
static int garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
- int need, Eterm* objv, int nobj);
+ int need, Eterm* objv, int nobj, int fcalls);
static int major_collection(Process* p, ErlHeapFragment *live_hf_end,
int need, Eterm* objv, int nobj, Uint *recl);
static int minor_collection(Process* p, ErlHeapFragment *live_hf_end,
@@ -146,7 +147,8 @@ static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size,
static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size);
static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size);
static void move_msgq_to_heap(Process *p);
-
+static int reached_max_heap_size(Process *p, Uint total_heap_size,
+ Uint extra_heap_size, Uint extra_old_heap_size);
static void init_gc_info(ErtsGCInfo *gcip);
#ifdef HARDDEBUG
@@ -392,15 +394,15 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end,
regs = ERTS_PROC_GET_SCHDATA(p)->x_reg_array;
}
#endif
- cost = garbage_collect(p, live_hf_end, 0, regs, p->arity);
+ cost = garbage_collect(p, live_hf_end, 0, regs, p->arity, p->fcalls);
} else {
- cost = garbage_collect(p, live_hf_end, 0, regs, arity);
+ cost = garbage_collect(p, live_hf_end, 0, regs, arity, p->fcalls);
}
} else {
Eterm val[1];
val[0] = result;
- cost = garbage_collect(p, live_hf_end, 0, val, 1);
+ cost = garbage_collect(p, live_hf_end, 0, val, 1, p->fcalls);
result = val[0];
}
BUMP_REDS(p, cost);
@@ -431,7 +433,7 @@ static ERTS_INLINE void reset_active_writer(Process *p)
#define ERTS_ABANDON_HEAP_COST 10
static int
-delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need)
+delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int fcalls)
{
ErlHeapFragment *hfrag;
Eterm *orig_heap, *orig_hend, *orig_htop, *orig_stop;
@@ -506,12 +508,16 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need)
/* Make sure that we do a proper GC as soon as possible... */
p->flags |= F_FORCE_GC;
- reds_left = ERTS_BIF_REDS_LEFT(p);
+ reds_left = ERTS_REDS_LEFT(p, fcalls);
+ ASSERT(CONTEXT_REDS - reds_left >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds);
+
if (reds_left > ERTS_ABANDON_HEAP_COST) {
int vreds = reds_left - ERTS_ABANDON_HEAP_COST;
- ERTS_VBUMP_REDS(p, vreds);
+ ERTS_PROC_GET_SCHDATA((p))->virtual_reds += vreds;
}
- return ERTS_ABANDON_HEAP_COST;
+
+ ASSERT(CONTEXT_REDS >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds);
+ return reds_left;
}
static ERTS_FORCE_INLINE Uint
@@ -570,19 +576,26 @@ young_gen_usage(Process *p)
*/
static int
garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
- int need, Eterm* objv, int nobj)
+ int need, Eterm* objv, int nobj, int fcalls)
{
Uint reclaimed_now = 0;
+ Eterm gc_trace_end_tag;
int reds;
ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */
ErtsSchedulerData *esdp;
+ erts_aint32_t state;
ERTS_MSACC_PUSH_STATE_M();
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
#endif
- if (p->flags & (F_DISABLE_GC|F_DELAY_GC))
- return delay_garbage_collection(p, live_hf_end, need);
+ ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls)
+ >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds);
+
+ state = erts_smp_atomic32_read_nob(&p->state);
+
+ if (p->flags & (F_DISABLE_GC|F_DELAY_GC) || state & ERTS_PSFLG_EXITING)
+ return delay_garbage_collection(p, live_hf_end, need, fcalls);
if (p->abandoned_heap)
live_hf_end = ERTS_INVALID_HFRAG_PTR;
@@ -593,10 +606,6 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
esdp = erts_get_scheduler_data();
- if (IS_TRACED_FL(p, F_TRACE_GC)) {
- trace_gc(p, am_gc_start);
- }
-
erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
if (erts_system_monitor_long_gc != 0)
start_time = erts_get_monotonic_time(esdp);
@@ -619,18 +628,29 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
*/
if (GEN_GCS(p) < MAX_GEN_GCS(p) && !(FLAGS(p) & F_NEED_FULLSWEEP)) {
- DTRACE2(gc_minor_start, pidbuf, need);
- reds = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now);
- DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
- if (reds < 0)
- goto do_major_collection;
- }
- else {
- do_major_collection:
+ if (IS_TRACED_FL(p, F_TRACE_GC)) {
+ trace_gc(p, am_gc_minor_start, need, THE_NON_VALUE);
+ }
+ DTRACE2(gc_minor_start, pidbuf, need);
+ reds = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now);
+ DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
+ if (reds == -1) {
+ if (IS_TRACED_FL(p, F_TRACE_GC)) {
+ trace_gc(p, am_gc_minor_end, reclaimed_now, THE_NON_VALUE);
+ }
+ goto do_major_collection;
+ }
+ gc_trace_end_tag = am_gc_minor_end;
+ } else {
+do_major_collection:
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC_FULL);
- DTRACE2(gc_major_start, pidbuf, need);
- reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now);
- DTRACE2(gc_major_end, pidbuf, reclaimed_now);
+ if (IS_TRACED_FL(p, F_TRACE_GC)) {
+ trace_gc(p, am_gc_major_start, need, THE_NON_VALUE);
+ }
+ DTRACE2(gc_major_start, pidbuf, need);
+ reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now);
+ DTRACE2(gc_major_end, pidbuf, reclaimed_now);
+ gc_trace_end_tag = am_gc_major_end;
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC);
}
@@ -646,8 +666,31 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
+ /* Max heap size has been reached and the process was configured
+ to be killed, so we kill it and set it in a delayed garbage
+ collecting state. There should be no gc_end trace or
+ long_gc/large_gc triggers when this happens as process was
+ killed before a GC could be done. */
+ if (reds == -2) {
+ ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
+
+ erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ erts_send_exit_signal(p, p->common.id, p, &locks,
+ am_kill, NIL, NULL, 0);
+ erts_smp_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+
+ /* erts_send_exit_signal looks for ERTS_PSFLG_GC, so
+ we have to remove it after the signal is sent */
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
+
+ /* We have to make sure that we have space for need on the heap */
+ return delay_garbage_collection(p, live_hf_end, need, fcalls);
+ }
+
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
+
if (IS_TRACED_FL(p, F_TRACE_GC)) {
- trace_gc(p, am_gc_end);
+ trace_gc(p, gc_trace_end_tag, reclaimed_now, THE_NON_VALUE);
}
if (erts_system_monitor_long_gc != 0) {
@@ -700,16 +743,23 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
}
int
-erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj)
+erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj, int fcalls)
{
- return garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj);
+ int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, fcalls);
+ int reds_left = ERTS_REDS_LEFT(p, fcalls);
+ if (reds > reds_left)
+ reds = reds_left;
+ ASSERT(CONTEXT_REDS - (reds_left - reds) >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds);
+ return reds;
}
void
erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
{
- int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj);
+ int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, p->fcalls);
BUMP_REDS(p, reds);
+ ASSERT(CONTEXT_REDS - ERTS_BIF_REDS_LEFT(p)
+ >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds);
}
/*
@@ -744,6 +794,7 @@ erts_garbage_collect_hibernate(Process* p)
heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz;
+
heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP,
sizeof(Eterm)*heap_size);
htop = heap;
@@ -1014,6 +1065,34 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end,
Uint size_before = young_gen_usage(p);
/*
+ * Check if we have gone past the max heap size limit
+ */
+
+ if (MAX_HEAP_SIZE_GET(p)) {
+ Uint heap_size = size_before,
+ /* Note that we also count the un-allocated area
+ in between the stack and heap */
+ stack_size = HEAP_END(p) - HEAP_TOP(p),
+ extra_heap_size,
+ extra_old_heap_size = 0;
+
+ /* Add potential old heap size */
+ if (OLD_HEAP(p) == NULL && mature_size != 0) {
+ extra_old_heap_size = erts_next_heap_size(size_before, 1);
+ heap_size += extra_old_heap_size;
+ } else if (OLD_HEAP(p))
+ heap_size += OLD_HEND(p) - OLD_HEAP(p);
+
+ /* Add potential new young heap size */
+ extra_heap_size = next_heap_size(p, stack_size + size_before, 0);
+ heap_size += extra_heap_size;
+
+ if (heap_size > MAX_HEAP_SIZE_GET(p))
+ if (reached_max_heap_size(p, heap_size, extra_heap_size, extra_old_heap_size))
+ return -2;
+ }
+
+ /*
* Allocate an old heap if we don't have one and if we'll need one.
*/
@@ -1122,6 +1201,16 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end,
ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0));
ASSERT(MBUF(p) == NULL);
+ /* The heap usage during GC should be larger than what we end up
+ after a GC, even if we grow it. If this assertion is not true
+ we have to check size in grow_new_heap and potentially kill the
+ process from there */
+ ASSERT(!MAX_HEAP_SIZE_GET(p) ||
+ !(MAX_HEAP_SIZE_FLAGS_GET(p) & MAX_HEAP_SIZE_KILL) ||
+ MAX_HEAP_SIZE_GET(p) > (young_gen_usage(p) +
+ (OLD_HEND(p) - OLD_HEAP(p)) +
+ (HEAP_END(p) - HEAP_TOP(p))));
+
return gc_cost(size_after, adjust_size);
}
@@ -1440,6 +1529,25 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
if (new_sz == HEAP_SIZE(p) && FLAGS(p) & F_HEAP_GROW) {
new_sz = next_heap_size(p, HEAP_SIZE(p), 1);
}
+
+
+ if (MAX_HEAP_SIZE_GET(p)) {
+ Uint heap_size = size_before;
+
+ /* Add unused space in old heap */
+ heap_size += OLD_HEND(p) - OLD_HTOP(p);
+
+ /* Add stack + unused space in young heap */
+ heap_size += HEAP_END(p) - HEAP_TOP(p);
+
+ /* Add size of new young heap */
+ heap_size += new_sz;
+
+ if (MAX_HEAP_SIZE_GET(p) < heap_size)
+ if (reached_max_heap_size(p, heap_size, new_sz, 0))
+ return -2;
+ }
+
FLAGS(p) &= ~(F_HEAP_GROW|F_NEED_FULLSWEEP);
n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
@@ -2921,7 +3029,7 @@ reply_gc_info(void *vgcirp)
hpp = &hp;
}
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (gcirp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -2969,7 +3077,9 @@ erts_gc_info_request(Process *c_p)
}
Eterm
-erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp)
+erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp,
+ Uint extra_heap_block,
+ Uint extra_old_heap_block_size)
{
ERTS_DECL_AM(bin_vheap_size);
ERTS_DECL_AM(bin_vheap_block_size);
@@ -2992,8 +3102,9 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp)
AM_bin_old_vheap_block_size
};
UWord values[] = {
- OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0,
- HEAP_SIZE(p),
+ OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) + extra_old_heap_block_size
+ : extra_old_heap_block_size,
+ HEAP_SIZE(p) + extra_heap_block,
MBUF_SIZE(p),
HIGH_WATER(p) - HEAP_START(p),
STACK_START(p) - p->stop,
@@ -3006,10 +3117,30 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp)
};
Eterm res = THE_NON_VALUE;
+ ErtsMessage *mp;
ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags));
ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS);
+ if (p->abandoned_heap) {
+ Eterm *htop, *heap;
+ ERTS_GET_ORIG_HEAP(p, heap, htop);
+ values[3] = HIGH_WATER(p) - heap;
+ values[6] = htop - heap;
+ }
+
+ if (p->flags & F_ON_HEAP_MSGQ) {
+ /* If on heap messages in the internal queue are counted
+ as being part of the heap, so we have to add them to the
+ am_mbuf_size value. process_info(total_heap_size) should
+ be the same as adding old_heap_block_size + heap_block_size
+ + mbuf_size.
+ */
+ for (mp = p->msg.first; mp; mp = mp->next)
+ if (mp->data.attached)
+ values[2] += erts_msg_attached_data_size(mp);
+ }
+
res = erts_bld_atom_uword_2tup_list(hpp,
sizep,
sizeof(values)/sizeof(*values),
@@ -3019,6 +3150,130 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp)
return res;
}
+static int
+reached_max_heap_size(Process *p, Uint total_heap_size,
+ Uint extra_heap_size, Uint extra_old_heap_size)
+{
+ Uint max_heap_flags = MAX_HEAP_SIZE_FLAGS_GET(p);
+ if (IS_TRACED_FL(p, F_TRACE_GC) ||
+ max_heap_flags & MAX_HEAP_SIZE_LOG) {
+ Eterm msg;
+ Uint size = 0;
+ Eterm *o_hp , *hp;
+ erts_process_gc_info(p, &size, NULL, extra_heap_size,
+ extra_old_heap_size);
+ o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, size * sizeof(Eterm));
+ msg = erts_process_gc_info(p, NULL, &hp, extra_heap_size,
+ extra_old_heap_size);
+
+ if (max_heap_flags & MAX_HEAP_SIZE_LOG) {
+ int alive = erts_is_alive;
+ erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
+ Eterm *o_hp, *hp, args = NIL;
+
+ /* Build the format message */
+ erts_dsprintf(dsbufp, " Process: ~p ");
+ if (alive)
+ erts_dsprintf(dsbufp, "on node ~p");
+ erts_dsprintf(dsbufp, "~n Context: maximum heap size reached~n");
+ erts_dsprintf(dsbufp, " Max heap size: ~p~n");
+ erts_dsprintf(dsbufp, " Total heap size: ~p~n");
+ erts_dsprintf(dsbufp, " Kill: ~p~n");
+ erts_dsprintf(dsbufp, " Error Logger: ~p~n");
+ erts_dsprintf(dsbufp, " GC Info: ~p~n");
+
+ /* Build the args in reverse order */
+ o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, 2*(alive ? 7 : 6) * sizeof(Eterm));
+ args = CONS(hp, msg, args); hp += 2;
+ args = CONS(hp, am_true, args); hp += 2;
+ args = CONS(hp, (max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false), args); hp += 2;
+ args = CONS(hp, make_small(total_heap_size), args); hp += 2;
+ args = CONS(hp, make_small(MAX_HEAP_SIZE_GET(p)), args); hp += 2;
+ if (alive) {
+ args = CONS(hp, erts_this_node->sysname, args); hp += 2;
+ }
+ args = CONS(hp, p->common.id, args); hp += 2;
+
+ erts_send_error_term_to_logger(p->group_leader, dsbufp, args);
+ erts_free(ERTS_ALC_T_TMP, o_hp);
+ }
+
+ if (IS_TRACED_FL(p, F_TRACE_GC))
+ trace_gc(p, am_gc_max_heap_size, 0, msg);
+
+ erts_free(ERTS_ALC_T_TMP, o_hp);
+ }
+ /* returns true if we should kill the process */
+ return max_heap_flags & MAX_HEAP_SIZE_KILL;
+}
+
+Eterm
+erts_max_heap_size_map(Sint max_heap_size, Uint max_heap_flags,
+ Eterm **hpp, Uint *sz)
+{
+ if (!hpp) {
+ *sz += (2*3 + 1 + MAP_HEADER_FLATMAP_SZ);
+ return THE_NON_VALUE;
+ } else {
+ Eterm *hp = *hpp;
+ Eterm keys = TUPLE3(hp, am_error_logger, am_kill, am_size);
+ flatmap_t *mp;
+ hp += 4;
+ mp = (flatmap_t*) hp;
+ mp->thing_word = MAP_HEADER_FLATMAP;
+ mp->size = 3;
+ mp->keys = keys;
+ hp += MAP_HEADER_FLATMAP_SZ;
+ *hp++ = max_heap_flags & MAX_HEAP_SIZE_LOG ? am_true : am_false;
+ *hp++ = max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false;
+ *hp++ = make_small(max_heap_size);
+ *hpp = hp;
+ return make_flatmap(mp);
+ }
+}
+
+int
+erts_max_heap_size(Eterm arg, Uint *max_heap_size, Uint *max_heap_flags)
+{
+ Sint sz;
+ *max_heap_flags = H_MAX_FLAGS;
+ if (is_small(arg)) {
+ sz = signed_val(arg);
+ *max_heap_flags = H_MAX_FLAGS;
+ } else if (is_map(arg)) {
+ const Eterm *size = erts_maps_get(am_size, arg);
+ const Eterm *kill = erts_maps_get(am_kill, arg);
+ const Eterm *log = erts_maps_get(am_error_logger, arg);
+ if (size && is_small(*size)) {
+ sz = signed_val(*size);
+ } else {
+ /* size is mandatory */
+ return 0;
+ }
+ if (kill) {
+ if (*kill == am_true)
+ *max_heap_flags |= MAX_HEAP_SIZE_KILL;
+ else if (*kill == am_false)
+ *max_heap_flags &= ~MAX_HEAP_SIZE_KILL;
+ else
+ return 0;
+ }
+ if (log) {
+ if (*log == am_true)
+ *max_heap_flags |= MAX_HEAP_SIZE_LOG;
+ else if (*log == am_false)
+ *max_heap_flags &= ~MAX_HEAP_SIZE_LOG;
+ else
+ return 0;
+ }
+ } else
+ return 0;
+ if (sz < 0)
+ return 0;
+ *max_heap_size = sz;
+ return 1;
+}
+
#if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG)
static int
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index 40b7c5d12c..54ea9ca3c0 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -135,11 +135,11 @@ typedef struct {
#define ERTS_PROCESS_GC_INFO_MAX_TERMS (11) /* number of elements in process_gc_info*/
#define ERTS_PROCESS_GC_INFO_MAX_SIZE \
(ERTS_PROCESS_GC_INFO_MAX_TERMS * (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE))
-Eterm erts_process_gc_info(struct process*, Uint *, Eterm **);
+Eterm erts_process_gc_info(struct process*, Uint *, Eterm **, Uint, Uint);
void erts_gc_info(ErtsGCInfo *gcip);
void erts_init_gc(void);
-int erts_garbage_collect_nobump(struct process*, int, Eterm*, int);
+int erts_garbage_collect_nobump(struct process*, int, Eterm*, int, int);
void erts_garbage_collect(struct process*, int, Eterm*, int);
void erts_garbage_collect_hibernate(struct process* p);
Eterm erts_gc_after_bif_call_lhf(struct process* p, ErlHeapFragment *live_hf_end,
@@ -155,5 +155,7 @@ void erts_offset_off_heap(struct erl_off_heap*, Sint, Eterm*, Eterm*);
void erts_offset_heap_ptr(Eterm*, Uint, Sint, Eterm*, Eterm*);
void erts_offset_heap(Eterm*, Uint, Sint, Eterm*, Eterm*);
void erts_free_heap_frags(struct process* p);
+Eterm erts_max_heap_size_map(Sint, Uint, Eterm **, Uint *);
+int erts_max_heap_size(Eterm, Uint *, Uint *);
#endif /* __ERL_GC_H__ */
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index 8e201d5711..c418762578 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -1247,8 +1247,8 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
if (!ERTS_PROC_IS_EXITING(proc)) {
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = tmr->btm.bp;
- erts_queue_message(proc, &proc_locks, mp,
- tmr->btm.message);
+ erts_queue_message(proc, proc_locks, mp,
+ tmr->btm.message, am_clock_service);
erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND);
queued_message = 1;
proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND;
@@ -1980,7 +1980,7 @@ access_sched_local_btm(Process *c_p, Eterm pid,
ERTS_HLT_ASSERT(hp + (async ? 4 : 3) == hp_end);
- erts_queue_message(proc, &proc_locks, mp, msg);
+ erts_queue_message(proc, proc_locks, mp, msg, am_clock_service);
if (c_p)
proc_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -2111,7 +2111,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
msg = TUPLE3(hp, tag, tref, res);
- erts_queue_message(c_p, &proc_locks, mp, msg);
+ erts_queue_message(c_p, proc_locks, mp, msg, am_clock_service);
proc_locks &= ~ERTS_PROC_LOCK_MAIN;
if (proc_locks)
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index dec9bdfedc..0649fb68de 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -164,6 +164,8 @@ int erts_use_sender_punish;
Uint display_items; /* no of items to display in traces etc */
int H_MIN_SIZE; /* The minimum heap grain */
int BIN_VH_MIN_SIZE; /* The minimum binary virtual*/
+int H_MAX_SIZE; /* The maximum heap size */
+int H_MAX_FLAGS; /* The maximum heap flags */
Uint32 erts_debug_flags; /* Debug flags. */
int erts_backtrace_depth; /* How many functions to show in a backtrace
@@ -206,15 +208,15 @@ int erts_no_line_info = 0; /* -L: Don't load line information */
ErtsModifiedTimings erts_modified_timings[] = {
/* 0 */ {make_small(0), CONTEXT_REDS, INPUT_REDUCTIONS},
- /* 1 */ {make_small(0), 2*CONTEXT_REDS, 2*INPUT_REDUCTIONS},
+ /* 1 */ {make_small(0), (3*CONTEXT_REDS)/4, 2*INPUT_REDUCTIONS},
/* 2 */ {make_small(0), CONTEXT_REDS/2, INPUT_REDUCTIONS/2},
- /* 3 */ {make_small(0), 3*CONTEXT_REDS, 3*INPUT_REDUCTIONS},
+ /* 3 */ {make_small(0), (7*CONTEXT_REDS)/8, 3*INPUT_REDUCTIONS},
/* 4 */ {make_small(0), CONTEXT_REDS/3, 3*INPUT_REDUCTIONS},
- /* 5 */ {make_small(0), 4*CONTEXT_REDS, INPUT_REDUCTIONS/2},
+ /* 5 */ {make_small(0), (10*CONTEXT_REDS)/11, INPUT_REDUCTIONS/2},
/* 6 */ {make_small(1), CONTEXT_REDS/4, 2*INPUT_REDUCTIONS},
- /* 7 */ {make_small(1), 5*CONTEXT_REDS, INPUT_REDUCTIONS/3},
+ /* 7 */ {make_small(1), (5*CONTEXT_REDS)/7, INPUT_REDUCTIONS/3},
/* 8 */ {make_small(10), CONTEXT_REDS/5, 3*INPUT_REDUCTIONS},
- /* 9 */ {make_small(10), 6*CONTEXT_REDS, INPUT_REDUCTIONS/4}
+ /* 9 */ {make_small(10), (6*CONTEXT_REDS)/7, INPUT_REDUCTIONS/4}
};
#define ERTS_MODIFIED_TIMING_LEVELS \
@@ -576,8 +578,14 @@ void erts_usage(void)
H_DEFAULT_SIZE);
erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n",
VH_DEFAULT_SIZE);
+ erts_fprintf(stderr, "-hmax size set maximum heap size in words (default %d)\n",
+ H_DEFAULT_MAX_SIZE);
+ erts_fprintf(stderr, "-hmaxk bool enable or disable kill at max heap size (default true)\n");
+ erts_fprintf(stderr, "-hmaxel bool enable or disable error_logger report at max heap size (default true)\n");
erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n",
erts_pd_initial_size);
+ erts_fprintf(stderr, "-hmqd val set default message queue data flag for processes,\n");
+ erts_fprintf(stderr, " valid values are: off_heap | on_heap | mixed\n");
/* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */
@@ -655,8 +663,6 @@ void erts_usage(void)
erts_fprintf(stderr, "-W<i|w|e> set error logger warnings mapping,\n");
erts_fprintf(stderr, " see error_logger documentation for details\n");
- erts_fprintf(stderr, "-xmqd val set default message queue data flag for processes,\n");
- erts_fprintf(stderr, " valid values are: off_heap | on_heap | mixed\n");
erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n");
erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024);
erts_fprintf(stderr, "-zdntgc time set delayed node table gc in seconds\n");
@@ -759,6 +765,8 @@ early_init(int *argc, char **argv) /*
erts_async_thread_suggested_stack_size = ERTS_ASYNC_THREAD_MIN_STACK_SIZE;
H_MIN_SIZE = H_DEFAULT_SIZE;
BIN_VH_MIN_SIZE = VH_DEFAULT_SIZE;
+ H_MAX_SIZE = H_DEFAULT_MAX_SIZE;
+ H_MAX_FLAGS = MAX_HEAP_SIZE_KILL|MAX_HEAP_SIZE_LOG;
erts_initialized = 0;
@@ -1484,9 +1492,13 @@ erl_start(int argc, char **argv)
char *sub_param = argv[i]+2;
/* set default heap size
*
- * h|ms - min_heap_size
- * h|mbs - min_bin_vheap_size
- * h|pds - erts_pd_initial_size
+ * h|ms - min_heap_size
+ * h|mbs - min_bin_vheap_size
+ * h|pds - erts_pd_initial_size
+ * h|mqd - message_queue_data
+ * h|max - max_heap_size
+ * h|maxk - max_heap_kill
+ * h|maxel - max_heap_error_logger
*
*/
if (has_prefix("mbs", sub_param)) {
@@ -1512,6 +1524,58 @@ erl_start(int argc, char **argv)
}
VERBOSE(DEBUG_SYSTEM, ("using initial process dictionary size %d\n",
erts_pd_initial_size));
+ } else if (has_prefix("mqd", sub_param)) {
+ arg = get_arg(sub_param+3, argv[i+1], &i);
+ if (sys_strcmp(arg, "mixed") == 0)
+ erts_default_spo_flags &= ~(SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ);
+ else if (sys_strcmp(arg, "on_heap") == 0) {
+ erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ;
+ erts_default_spo_flags |= SPO_ON_HEAP_MSGQ;
+ }
+ else if (sys_strcmp(arg, "off_heap") == 0) {
+ erts_default_spo_flags &= ~SPO_ON_HEAP_MSGQ;
+ erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ;
+ }
+ else {
+ erts_fprintf(stderr,
+ "Invalid message_queue_data flag: %s\n", arg);
+ erts_usage();
+ }
+ } else if (has_prefix("maxk", sub_param)) {
+ arg = get_arg(sub_param+4, argv[i+1], &i);
+ if (strcmp(arg,"true") == 0) {
+ H_MAX_FLAGS |= MAX_HEAP_SIZE_KILL;
+ } else if (strcmp(arg,"false") == 0) {
+ H_MAX_FLAGS &= ~MAX_HEAP_SIZE_KILL;
+ } else {
+ erts_fprintf(stderr, "bad max heap kill %s\n", arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM, ("using max heap kill %d\n", H_MAX_FLAGS));
+ } else if (has_prefix("maxel", sub_param)) {
+ arg = get_arg(sub_param+5, argv[i+1], &i);
+ if (strcmp(arg,"true") == 0) {
+ H_MAX_FLAGS |= MAX_HEAP_SIZE_LOG;
+ } else if (strcmp(arg,"false") == 0) {
+ H_MAX_FLAGS &= ~MAX_HEAP_SIZE_LOG;
+ } else {
+ erts_fprintf(stderr, "bad max heap error logger %s\n", arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM, ("using max heap log %d\n", H_MAX_FLAGS));
+ } else if (has_prefix("max", sub_param)) {
+ arg = get_arg(sub_param+3, argv[i+1], &i);
+ if ((H_MAX_SIZE = atoi(arg)) < 0) {
+ erts_fprintf(stderr, "bad max heap size %s\n", arg);
+ erts_usage();
+ }
+ if (H_MAX_SIZE < H_MIN_SIZE && H_MAX_SIZE) {
+ erts_fprintf(stderr, "max heap size (%s) is not allowed to be "
+ "smaller than min heap size (%d)\n",
+ arg, H_MIN_SIZE);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM, ("using max heap size %d\n", H_MAX_SIZE));
} else {
/* backward compatibility */
arg = get_arg(argv[i]+2, argv[i+1], &i);
@@ -2047,32 +2111,6 @@ erl_start(int argc, char **argv)
}
break;
- case 'x': {
- char *sub_param = argv[i]+2;
- if (has_prefix("mqd", sub_param)) {
- arg = get_arg(sub_param+3, argv[i+1], &i);
- if (sys_strcmp(arg, "mixed") == 0)
- erts_default_spo_flags &= ~(SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ);
- else if (sys_strcmp(arg, "on_heap") == 0) {
- erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ;
- erts_default_spo_flags |= SPO_ON_HEAP_MSGQ;
- }
- else if (sys_strcmp(arg, "off_heap") == 0) {
- erts_default_spo_flags &= ~SPO_ON_HEAP_MSGQ;
- erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ;
- }
- else {
- erts_fprintf(stderr,
- "Invalid message_queue_data flag: %s\n", arg);
- erts_usage();
- }
- } else {
- erts_fprintf(stderr, "bad -x option %s\n", argv[i]);
- erts_usage();
- }
- break;
- }
-
case 'z': {
char *sub_param = argv[i]+2;
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 03a96cb00a..8efc983f04 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -54,6 +54,7 @@
* - maps:new/0
* - maps:put/3
* - maps:remove/2
+ * - maps:take/2
* - maps:to_list/1
* - maps:update/3
* - maps:values/1
@@ -93,7 +94,7 @@ static Uint hashmap_subtree_size(Eterm node);
static Eterm hashmap_to_list(Process *p, Eterm map);
static Eterm hashmap_keys(Process *p, Eterm map);
static Eterm hashmap_values(Process *p, Eterm map);
-static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node, Eterm *value);
static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size);
static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size);
static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int reject_dupkeys);
@@ -1521,10 +1522,45 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
BIF_ERROR(BIF_P, BADMAP);
}
-/* maps:remove/3 */
+/* maps:take/2 */
-int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
+BIF_RETTYPE maps_take_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm res, map, val;
+ if (erts_maps_take(BIF_P, BIF_ARG_1, BIF_ARG_2, &map, &val)) {
+ Eterm *hp = HAlloc(BIF_P, 3);
+ res = make_tuple(hp);
+ *hp++ = make_arityval(2);
+ *hp++ = val;
+ *hp++ = map;
+ BIF_RET(res);
+ }
+ BIF_RET(am_error);
+ }
+ BIF_P->fvalue = BIF_ARG_2;
+ BIF_ERROR(BIF_P, BADMAP);
+}
+
+/* maps:remove/2 */
+
+BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm res;
+ (void) erts_maps_take(BIF_P, BIF_ARG_1, BIF_ARG_2, &res, NULL);
+ BIF_RET(res);
+ }
+ BIF_P->fvalue = BIF_ARG_2;
+ BIF_ERROR(BIF_P, BADMAP);
+}
+
+/* erts_maps_take
+ * return 1 if key is found, otherwise 0
+ * If the key is not found res (output map) will be map (input map)
+ */
+int erts_maps_take(Process *p, Eterm key, Eterm map,
+ Eterm *res, Eterm *value) {
Uint32 hx;
+ Eterm ret;
if (is_flatmap(map)) {
Sint n;
Uint need;
@@ -1537,7 +1573,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
if (n == 0) {
*res = map;
- return 1;
+ return 0;
}
ks = flatmap_get_keys(mp);
@@ -1564,6 +1600,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
if (is_immed(key)) {
while (1) {
if (*ks == key) {
+ if (value) *value = *vs;
goto found_key;
} else if (--n) {
*mhp++ = *vs++;
@@ -1574,6 +1611,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
} else {
while(1) {
if (EQ(*ks, key)) {
+ if (value) *value = *vs;
goto found_key;
} else if (--n) {
*mhp++ = *vs++;
@@ -1589,7 +1627,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
HRelease(p, hp_start + need, hp_start);
*res = map;
- return 1;
+ return 0;
found_key:
/* Copy rest of keys and values */
@@ -1601,19 +1639,13 @@ found_key:
}
ASSERT(is_hashmap(map));
hx = hashmap_make_hash(key);
- *res = hashmap_delete(p, hx, key, map);
- return 1;
-}
-
-BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
- if (is_map(BIF_ARG_2)) {
- Eterm res;
- if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) {
- BIF_RET(res);
- }
+ ret = hashmap_delete(p, hx, key, map, value);
+ if (is_value(ret)) {
+ *res = ret;
+ return 1;
}
- BIF_P->fvalue = BIF_ARG_2;
- BIF_ERROR(BIF_P, BADMAP);
+ *res = map;
+ return 0;
}
int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) {
@@ -2322,7 +2354,8 @@ static Eterm hashmap_values(Process* p, Eterm node) {
return res;
}
-static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key,
+ Eterm map, Eterm *value) {
Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
Eterm *ptr;
Eterm hdr, res = map, node = map;
@@ -2337,8 +2370,12 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
switch(primary_tag(node)) {
case TAG_PRIMARY_LIST:
if (EQ(CAR(list_val(node)), key)) {
+ if (value) {
+ *value = CDR(list_val(node));
+ }
goto unroll;
}
+ res = THE_NON_VALUE;
goto not_found;
case TAG_PRIMARY_BOXED:
ptr = boxed_val(node);
@@ -2365,6 +2402,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
n = hashmap_bitcount(hval);
} else {
/* not occupied */
+ res = THE_NON_VALUE;
goto not_found;
}
@@ -2394,6 +2432,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
break;
}
/* not occupied */
+ res = THE_NON_VALUE;
goto not_found;
default:
erts_exit(ERTS_ERROR_EXIT, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 7af9100906..8b5c9582ba 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -82,6 +82,7 @@ struct ErtsEStack_;
Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map);
int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res);
int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
+int erts_maps_take(Process *p, Eterm key, Eterm map, Eterm *res, Eterm *value);
Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
Eterm node, int is_update);
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 9beff52835..9bb6e40a11 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -32,6 +32,7 @@
#include "erl_process.h"
#include "erl_binary.h"
#include "dtrace-wrapper.h"
+#include "beam_bp.h"
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref,
ErtsMessageRef,
@@ -251,9 +252,10 @@ erts_realloc_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_s
void
erts_queue_dist_message(Process *rcvr,
- ErtsProcLocks *rcvr_locks,
+ ErtsProcLocks rcvr_locks,
ErtsDistExternal *dist_ext,
- Eterm token)
+ Eterm token,
+ Eterm from)
{
ErtsMessage* mp;
#ifdef USE_VM_PROBES
@@ -265,7 +267,7 @@ erts_queue_dist_message(Process *rcvr,
erts_aint_t state;
#endif
- ERTS_SMP_LC_ASSERT(*rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
+ ERTS_SMP_LC_ASSERT(rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
mp = erts_alloc_message(0, NULL);
mp->data.dist_ext = dist_ext;
@@ -280,10 +282,10 @@ erts_queue_dist_message(Process *rcvr,
ERL_MESSAGE_TOKEN(mp) = token;
#ifdef ERTS_SMP
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) {
if (erts_smp_proc_trylock(rcvr, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
- if (*rcvr_locks & ERTS_PROC_LOCK_STATUS) {
+ if (rcvr_locks & ERTS_PROC_LOCK_STATUS) {
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_STATUS);
need_locks |= ERTS_PROC_LOCK_STATUS;
}
@@ -293,7 +295,7 @@ erts_queue_dist_message(Process *rcvr,
state = erts_smp_atomic32_read_acqb(&rcvr->state);
if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
/* Drop message if receiver is exiting or has a pending exit ... */
erts_cleanup_messages(mp);
@@ -301,10 +303,13 @@ erts_queue_dist_message(Process *rcvr,
else
#endif
if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) {
+ if (from == am_Empty)
+ from = dist_ext->dep->sysname;
+
/* Ahh... need to decode it in order to trace it... */
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
- if (!erts_decode_dist_message(rcvr, *rcvr_locks, mp, 0))
+ if (!erts_decode_dist_message(rcvr, rcvr_locks, mp, 0))
erts_free_message(mp);
else {
Eterm msg = ERL_MESSAGE_TERM(mp);
@@ -324,7 +329,7 @@ erts_queue_dist_message(Process *rcvr,
tok_label, tok_lastcnt, tok_serial);
}
#endif
- erts_queue_message(rcvr, rcvr_locks, mp, msg);
+ erts_queue_message(rcvr, rcvr_locks, mp, msg, from);
}
}
else {
@@ -351,12 +356,12 @@ erts_queue_dist_message(Process *rcvr,
LINK_MESSAGE(rcvr, mp, &mp->next, 1);
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
erts_proc_notify_new_message(rcvr,
#ifdef ERTS_SMP
- *rcvr_locks
+ rcvr_locks
#else
0
#endif
@@ -366,14 +371,15 @@ erts_queue_dist_message(Process *rcvr,
/* Add messages last in message queue */
static Sint
-queue_messages(Process *c_p,
- Process* receiver,
+queue_messages(Process* receiver,
erts_aint32_t *receiver_state,
- ErtsProcLocks *receiver_locks,
+ ErtsProcLocks receiver_locks,
ErtsMessage* first,
ErtsMessage** last,
- Uint len)
+ Uint len,
+ Eterm from)
{
+ ErtsTracingEvent* te;
Sint res;
int locked_msgq = 0;
erts_aint32_t state;
@@ -386,12 +392,12 @@ queue_messages(Process *c_p,
#ifdef ERTS_SMP
#ifdef ERTS_ENABLE_LOCK_CHECK
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(receiver) < ERTS_PROC_LOCK_MSGQ ||
- *receiver_locks == erts_proc_lc_my_proc_locks(receiver));
+ receiver_locks == erts_proc_lc_my_proc_locks(receiver));
#endif
- if (!(*receiver_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (!(receiver_locks & ERTS_PROC_LOCK_MSGQ)) {
if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
- ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
+ ErtsProcLocks need_locks;
if (receiver_state)
state = *receiver_state;
@@ -400,10 +406,11 @@ queue_messages(Process *c_p,
if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
goto exiting;
- if (*receiver_locks & ERTS_PROC_LOCK_STATUS) {
- erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS);
- need_locks |= ERTS_PROC_LOCK_STATUS;
+ need_locks = receiver_locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ if (need_locks) {
+ erts_smp_proc_unlock(receiver, need_locks);
}
+ need_locks |= ERTS_PROC_LOCK_MSGQ;
erts_smp_proc_lock(receiver, need_locks);
}
locked_msgq = 1;
@@ -426,7 +433,7 @@ queue_messages(Process *c_p,
res = receiver->msg.len;
#ifdef ERTS_SMP
- if (*receiver_locks & ERTS_PROC_LOCK_MAIN) {
+ if (receiver_locks & ERTS_PROC_LOCK_MAIN) {
/*
* We move 'in queue' to 'private queue' and place
* message at the end of 'private queue' in order
@@ -445,7 +452,10 @@ queue_messages(Process *c_p,
LINK_MESSAGE(receiver, first, last, len);
}
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
+ if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)
+ && (te = &erts_receive_tracing[erts_active_bp_ix()],
+ te->on)) {
+
ErtsMessage *msg = first;
#ifdef USE_VM_PROBES
@@ -468,19 +478,18 @@ queue_messages(Process *c_p,
tok_label, tok_lastcnt, tok_serial);
}
#endif
-
while (msg) {
- trace_receive(receiver, ERL_MESSAGE_TERM(msg));
+ trace_receive(receiver, from, ERL_MESSAGE_TERM(msg), te);
msg = msg->next;
}
}
-
- if (locked_msgq)
+ if (locked_msgq) {
erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
+ }
#ifdef ERTS_SMP
- erts_proc_notify_new_message(receiver, *receiver_locks);
+ erts_proc_notify_new_message(receiver, receiver_locks);
#else
erts_proc_notify_new_message(receiver, 0);
ERTS_HOLE_CHECK(receiver);
@@ -489,31 +498,31 @@ queue_messages(Process *c_p,
}
static Sint
-queue_message(Process *c_p,
- Process* receiver,
+queue_message(Process* receiver,
erts_aint32_t *receiver_state,
- ErtsProcLocks *receiver_locks,
- ErtsMessage* mp, Eterm msg)
+ ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from)
{
ERL_MESSAGE_TERM(mp) = msg;
- return queue_messages(c_p, receiver, receiver_state, receiver_locks,
- mp, &mp->next, 1 );
+ return queue_messages(receiver, receiver_state, receiver_locks,
+ mp, &mp->next, 1, from);
}
Sint
-erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks,
- ErtsMessage* mp, Eterm msg)
+erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from)
{
- return queue_message(NULL, receiver, NULL, receiver_locks, mp, msg);
+ return queue_message(receiver, NULL, receiver_locks, mp, msg, from);
}
Sint
-erts_queue_messages(Process* receiver, ErtsProcLocks *receiver_locks,
- ErtsMessage* first, ErtsMessage** last, Uint len)
+erts_queue_messages(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* first, ErtsMessage** last, Uint len,
+ Eterm from)
{
- return queue_messages(NULL, receiver, NULL, receiver_locks,
- first, last, len);
+ return queue_messages(receiver, NULL, receiver_locks,
+ first, last, len, from);
}
void
@@ -830,11 +839,11 @@ erts_send_message(Process* sender,
#ifdef USE_VM_PROBES
ERL_MESSAGE_DT_UTAG(mp) = utag;
#endif
- res = queue_message(sender,
- receiver,
+ res = queue_message(receiver,
&receiver_state,
- receiver_locks,
- mp, message);
+ *receiver_locks,
+ mp, message,
+ sender->common.id);
BM_SWAP_TIMER(send,system);
@@ -891,7 +900,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL);
temptoken = copy_struct(token, sz_token, &hp, ohp);
ERL_MESSAGE_TOKEN(mp) = temptoken;
- erts_queue_message(to, to_locksp, mp, save);
+ erts_queue_message(to, *to_locksp, mp, save, am_system);
} else {
sz_from = IS_CONST(from) ? 0 : size_object(from);
#ifdef SHCOPY_SEND
@@ -913,7 +922,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
? from
: copy_struct(from, sz_from, &hp, ohp));
save = TUPLE3(hp, am_EXIT, from_copy, mess);
- erts_queue_message(to, to_locksp, mp, save);
+ erts_queue_message(to, *to_locksp, mp, save, am_system);
}
}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 608cf552a2..851ac37fda 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -295,10 +295,10 @@ ErlHeapFragment* new_message_buffer(Uint);
ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint,
Eterm *, Uint);
void free_message_buffer(ErlHeapFragment *);
-void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm);
-Sint erts_queue_message(Process*, ErtsProcLocks*,ErtsMessage*, Eterm);
-Sint erts_queue_messages(Process*, ErtsProcLocks*,
- ErtsMessage*, ErtsMessage**, Uint);
+void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm);
+Sint erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
+Sint erts_queue_messages(Process*, ErtsProcLocks,
+ ErtsMessage*, ErtsMessage**, Uint, Eterm);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp);
diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c
index d0f305900a..0e625f213b 100644
--- a/erts/emulator/beam/erl_msacc.c
+++ b/erts/emulator/beam/erl_msacc.c
@@ -257,7 +257,7 @@ static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) {
if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx);
- erts_queue_message(rp, &rp_locks, msgp, msg);
+ erts_queue_message(rp, rp_locks, msgp, msg, am_system);
if (esdp && msaccrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 73c0eb8eba..a695a028ba 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -366,7 +366,7 @@ int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks)
rp_locks = 0;
if (rp->common.id == c_p->common.id)
rp_locks = c_p_locks;
- erts_queue_messages(rp, &rp_locks, first, last, len);
+ erts_queue_messages(rp, rp_locks, first, last, len, c_p->common.id);
if (rp->common.id == c_p->common.id)
rp_locks &= ~c_p_locks;
if (rp_locks)
@@ -405,7 +405,10 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ErlNifEnv* msg_env, ERL_NIF_TERM msg)
{
struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env;
- ErtsProcLocks rp_locks = 0, lc_locks = 0, c_p_locks = ERTS_PROC_LOCK_MAIN;
+ ErtsProcLocks rp_locks = 0;
+#ifdef ERTS_SMP
+ ErtsProcLocks lc_locks = 0;
+#endif
Process* rp;
Process* c_p;
ErtsMessage *mp;
@@ -417,7 +420,7 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (env != NULL) {
c_p = env->proc;
if (receiver == c_p->common.id) {
- rp_locks = c_p_locks;
+ rp_locks = ERTS_PROC_LOCK_MAIN;
flush_me = 1;
}
}
@@ -470,14 +473,8 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND))
trace_send(c_p, receiver, msg);
-
-#ifndef ERTS_SMP
}
-#endif
-
- erts_queue_message(rp, &rp_locks, mp, msg);
#ifdef ERTS_SMP
- }
else {
/* This clause is taken when the nif is called in the context
of a traced process. We do not know which locks we have
@@ -502,8 +499,6 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
#ifdef ERTS_ENABLE_LOCK_CHECK
lc_locks = erts_proc_lc_my_proc_locks(rp);
rp_locks |= lc_locks;
- if (receiver == c_p->common.id)
- c_p_locks |= lc_locks;
#endif
if (ERTS_FORCE_ENIF_SEND_DELAY() || msgq ||
rp_locks & ERTS_PROC_LOCK_MSGQ ||
@@ -532,19 +527,25 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
msgq->last = &mp->next;
erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
}
+ goto done;
} else {
erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
rp_locks &= ~ERTS_PROC_LOCK_TRACE;
rp_locks |= ERTS_PROC_LOCK_MSGQ;
- erts_queue_message(rp, &rp_locks, mp, msg);
}
}
-#endif
+#endif /* ERTS_SMP */
+
+ erts_queue_message(rp, rp_locks, mp, msg,
+ c_p ? c_p->common.id : am_undefined);
+#ifdef ERTS_SMP
+done:
if (c_p == rp)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
if (rp_locks & ~lc_locks)
erts_smp_proc_unlock(rp, rp_locks & ~lc_locks);
+#endif
if (!scheduler)
erts_proc_dec_refc(rp);
if (flush_me) {
@@ -2443,14 +2444,13 @@ int enif_make_map_remove(ErlNifEnv* env,
Eterm key,
Eterm *map_out)
{
- int res;
if (!is_map(map_in)) {
return 0;
}
flush_env(env);
- res = erts_maps_remove(env->proc, key, map_in, map_out);
+ (void) erts_maps_take(env->proc, key, map_in, map_out, NULL);
cache_env(env);
- return res;
+ return 1;
}
int enif_map_iterator_create(ErlNifEnv *env,
@@ -2781,7 +2781,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ErlNifEntry* entry = NULL;
ErlNifEnv env;
int i, err, encoding;
- Module* mod;
+ Module* module_p;
Eterm mod_atom;
const Atom* mod_atomp;
Eterm f_atom;
@@ -2791,6 +2791,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
int veto;
struct erl_module_nif* lib = NULL;
int reload_warning = 0;
+ struct erl_module_instance* this_mi;
+ struct erl_module_instance* prev_mi;
encoding = erts_get_native_filename_encoding();
if (encoding == ERL_FILENAME_WIN_WCHAR) {
@@ -2824,21 +2826,29 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ASSERT(caller != NULL);
mod_atom = caller[0];
ASSERT(is_atom(mod_atom));
- mod=erts_get_module(mod_atom, erts_active_code_ix());
- ASSERT(mod != NULL);
+ module_p = erts_get_module(mod_atom, erts_active_code_ix());
+ ASSERT(module_p != NULL);
mod_atomp = atom_tab(atom_val(mod_atom));
init_func = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len);
if (init_func != NULL)
handle = init_func;
- if (!in_area(caller, mod->curr.code_hdr, mod->curr.code_length)) {
- ASSERT(in_area(caller, mod->old.code_hdr, mod->old.code_length));
+ if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
+ if (module_p->old.code_hdr->on_load_function_ptr) {
+ this_mi = &module_p->old;
+ prev_mi = &module_p->curr;
+ } else {
+ ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
+ "module '%T' not allowed", mod_atom);
+ goto error;
+ }
+ } else {
+ this_mi = &module_p->curr;
+ prev_mi = &module_p->old;
+ }
- ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
- "module '%T' not allowed", mod_atom);
- }
- else if (init_func == NULL &&
+ if (init_func == NULL &&
(err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) {
const char slogan[] = "Failed to load NIF library";
if (strstr(errdesc.str, lib_name) != NULL) {
@@ -2887,7 +2897,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) {
BeamInstr** code_pp;
if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1)
- || (code_pp = get_func_pp(mod->curr.code_hdr, f_atom, f->arity))==NULL) {
+ || (code_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) {
ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u",
mod_atom, f->name, f->arity);
}
@@ -2938,9 +2948,9 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_refc_init(&lib->rt_cnt, 0);
erts_refc_init(&lib->rt_dtor_cnt, 0);
ASSERT(opened_rt_list == NULL);
- lib->mod = mod;
+ lib->mod = module_p;
env.mod_nif = lib;
- if (mod->curr.nif != NULL) { /*************** Reload ******************/
+ if (this_mi->nif != NULL) { /*************** Reload ******************/
/*
* Repeated load_nif calls from same Erlang module instance ("reload")
* is deprecated and was only ment as a development feature not to
@@ -2948,16 +2958,16 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
*/
int k, old_incr = 0;
ErlNifFunc* old_func;
- lib->priv_data = mod->curr.nif->priv_data;
+ lib->priv_data = this_mi->nif->priv_data;
- ASSERT(mod->curr.nif->entry != NULL);
+ ASSERT(this_mi->nif->entry != NULL);
if (entry->reload == NULL) {
ret = load_nif_error(BIF_P,reload,"Reload not supported by this NIF library.");
goto error;
}
/* Check that no NIF is removed */
- old_func = mod->curr.nif->entry->funcs;
- for (k=0; k < mod->curr.nif->entry->num_of_funcs; k++) {
+ old_func = this_mi->nif->entry->funcs;
+ for (k=0; k < this_mi->nif->entry->num_of_funcs; k++) {
int incr = 0;
ErlNifFunc* f = entry->funcs;
for (i=0; i < entry->num_of_funcs; i++) {
@@ -2973,7 +2983,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
old_func->name, old_func->arity);
goto error;
}
- old_func = next_func(mod->curr.nif->entry, &old_incr, old_func);
+ old_func = next_func(this_mi->nif->entry, &old_incr, old_func);
}
erts_pre_nif(&env, BIF_P, lib, NULL);
veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2);
@@ -2983,24 +2993,24 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
else {
commit_opened_resource_types(lib);
- mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */
- erts_unload_nif(mod->curr.nif);
+ this_mi->nif->entry = NULL; /* to prevent 'unload' callback */
+ erts_unload_nif(this_mi->nif);
reload_warning = 1;
}
}
else {
lib->priv_data = NULL;
- if (mod->old.nif != NULL) { /**************** Upgrade ***************/
- void* prev_old_data = mod->old.nif->priv_data;
+ if (prev_mi->nif != NULL) { /**************** Upgrade ***************/
+ void* prev_old_data = prev_mi->nif->priv_data;
if (entry->upgrade == NULL) {
ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
- erts_pre_nif(&env, BIF_P, lib, NULL);
- veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2);
+ erts_pre_nif(&env, BIF_P, lib, NULL);
+ veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- mod->old.nif->priv_data = prev_old_data;
+ prev_mi->nif->priv_data = prev_old_data;
ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful.");
}
else
@@ -3025,12 +3035,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
int incr = 0;
ErlNifFunc* f = entry->funcs;
- mod->curr.nif = lib;
+ this_mi->nif = lib;
for (i=0; i < entry->num_of_funcs; i++)
{
BeamInstr* code_ptr;
erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1);
- code_ptr = *get_func_pp(mod->curr.code_hdr, f_atom, f->arity);
+ code_ptr = *get_func_pp(this_mi->code_hdr, f_atom, f->arity);
if (code_ptr[1] == 0) {
code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif);
@@ -3158,7 +3168,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee,
/* Verify that function is part of this module */
int i;
for (i = 0; i < mod->entry->num_of_funcs; i++)
- if (fun == mod->entry->funcs+i)
+ if (fun == &(mod->entry->funcs[i]))
break;
ASSERT(i < mod->entry->num_of_funcs);
if (p)
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index d485affa3b..e2f14c23bf 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1139,7 +1139,7 @@ reply_sched_wall_time(void *vswtrp)
hpp = &hp;
}
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (swtrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -1218,7 +1218,7 @@ reply_system_check(void *vscrp)
hpp = &hp;
msg = STORE_NC(hpp, ohp, scrp->ref);
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (scrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -6688,10 +6688,8 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st)
erts_aint32_t e;
n = e = a;
- if (a & ERTS_PSFLG_FREE) {
- res = 0;
+ if (a & ERTS_PSFLG_FREE)
goto cleanup; /* We don't want to schedule free processes... */
- }
enqueue = ERTS_ENQUEUE_NOT;
n |= ERTS_PSFLG_ACTIVE_SYS;
@@ -9240,6 +9238,21 @@ erts_set_process_priority(Process *p, Eterm value)
}
}
+static int
+scheduler_gc_proc(Process *c_p, int reds_left)
+{
+ int fcalls, reds;
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ fcalls = reds_left;
+ else
+ fcalls = reds_left - CONTEXT_REDS;
+ reds = erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls);
+ ASSERT(reds_left >= reds);
+ return reds;
+}
+
+
+
/*
* schedule() is called from BEAM (process_main()) or HiPE
* (hipe_mode_switch()) when the current process is to be
@@ -9318,6 +9331,7 @@ Process *schedule(Process *p, int calls)
#endif
reds = actual_reds = calls - esdp->virtual_reds;
+ ASSERT(actual_reds >= 0);
if (reds < ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST)
reds = ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST;
esdp->virtual_reds = 0;
@@ -9740,7 +9754,7 @@ Process *schedule(Process *p, int calls)
esdp->current_process = p;
-
+ calls = 0;
reds = context_reds;
#ifdef ERTS_SMP
@@ -9819,9 +9833,7 @@ Process *schedule(Process *p, int calls)
/* Migrate to dirty scheduler... */
sunlock_sched_out_proc:
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- p->fcalls = reds;
goto sched_out_proc;
-
}
}
else {
@@ -9856,8 +9868,6 @@ Process *schedule(Process *p, int calls)
#endif /* ERTS_SMP */
- p->fcalls = reds;
-
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
if (IS_TRACED(p)) {
@@ -9890,14 +9900,15 @@ Process *schedule(Process *p, int calls)
* not allowed to execute system tasks.
*/
if (!(p->flags & F_DELAY_GC)) {
- reds -= execute_sys_tasks(p, &state, reds);
+ int cost = execute_sys_tasks(p, &state, reds);
+ calls += cost;
+ reds -= cost;
if (reds <= 0
#ifdef ERTS_DIRTY_SCHEDULERS
|| ERTS_SCHEDULER_IS_DIRTY(esdp)
|| (state & ERTS_PSFLGS_DIRTY_WORK)
#endif
) {
- p->fcalls = reds;
goto sched_out_proc;
}
}
@@ -9910,8 +9921,9 @@ Process *schedule(Process *p, int calls)
if (((state & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_ACTIVE)) != ERTS_PSFLG_ACTIVE)
- && !(state & ERTS_PSFLG_EXITING))
+ && !(state & ERTS_PSFLG_EXITING)) {
goto sched_out_proc;
+ }
n = e = state;
n &= ~ERTS_PSFLG_RUNNING_SYS;
@@ -9930,11 +9942,11 @@ Process *schedule(Process *p, int calls)
if (ERTS_IS_GC_DESIRED(p)) {
if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) {
- reds -= erts_garbage_collect_nobump(p, 0, p->arg_reg, p->arity);
- if (reds <= 0) {
- p->fcalls = reds;
+ int cost = scheduler_gc_proc(p, reds);
+ calls += cost;
+ reds -= cost;
+ if (reds <= 0)
goto sched_out_proc;
- }
}
}
@@ -9942,6 +9954,8 @@ Process *schedule(Process *p, int calls)
free_proxy_proc(proxy_p);
proxy_p = NULL;
}
+
+ p->fcalls = reds;
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
@@ -10010,7 +10024,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result)
ASSERT(hp_start + hsz == hp);
#endif
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, c_p->common.id);
if (c_p == rp)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -10211,21 +10225,24 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
else {
if (!garbage_collected) {
FLAGS(c_p) |= F_NEED_FULLSWEEP;
- reds -= erts_garbage_collect_nobump(c_p,
- 0,
- c_p->arg_reg,
- c_p->arity);
+ reds -= scheduler_gc_proc(c_p, reds);
garbage_collected = 1;
}
st_res = am_true;
}
break;
case ERTS_PSTT_CPC: {
+ int fcalls;
int cpc_reds = 0;
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ fcalls = reds;
+ else
+ fcalls = reds - CONTEXT_REDS;
st_res = erts_check_process_code(c_p,
st->arg[0],
unsigned_val(st->arg[1]),
- &cpc_reds);
+ &cpc_reds,
+ fcalls);
reds -= cpc_reds;
if (is_non_value(st_res)) {
/* Needed gc, but gc was disabled */
@@ -10257,6 +10274,9 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
*statep = state;
+ if (in_reds < reds)
+ return in_reds;
+
return in_reds - reds;
}
@@ -11001,9 +11021,13 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->min_heap_size = so->min_heap_size;
p->min_vheap_size = so->min_vheap_size;
p->max_gen_gcs = so->max_gen_gcs;
+ MAX_HEAP_SIZE_SET(p, so->max_heap_size);
+ MAX_HEAP_SIZE_FLAGS_SET(p, so->max_heap_flags);
} else {
p->min_heap_size = H_MIN_SIZE;
p->min_vheap_size = BIN_VH_MIN_SIZE;
+ MAX_HEAP_SIZE_SET(p, H_MAX_SIZE);
+ MAX_HEAP_SIZE_FLAGS_SET(p, H_MAX_FLAGS);
p->max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
}
p->schedule_count = 0;
@@ -11482,8 +11506,10 @@ delete_process(Process* p)
psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd);
- if (psd)
+ if (psd) {
+ erts_smp_atomic_set_nob(&p->psd, (erts_aint_t) NULL); /* Reduction counting depends on this... */
erts_free(ERTS_ALC_T_PSD, psd);
+ }
/* Clean binaries and funs */
erts_cleanup_offheap(&p->off_heap);
@@ -11578,7 +11604,8 @@ set_proc_exiting(Process *p,
p->i = (BeamInstr *) beam_exit;
#ifndef ERTS_SMP
- if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)) {
+ if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)
+ && !(state & ERTS_PSFLG_GC)) {
/*
* I non smp case:
*
@@ -11744,7 +11771,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
mess = copy_struct(exit_term, term_size, &hp, ohp);
#endif
- erts_queue_message(to, to_locksp, mp, mess);
+ erts_queue_message(to, *to_locksp, mp, mess, am_system);
} else {
Eterm temp_token;
Uint sz_token;
@@ -11765,7 +11792,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to);
temp_token = copy_struct(token, sz_token, &hp, ohp);
ERL_MESSAGE_TOKEN(mp) = temp_token;
- erts_queue_message(to, to_locksp, mp, mess);
+ erts_queue_message(to, *to_locksp, mp, mess, am_system);
}
}
@@ -12577,6 +12604,8 @@ erts_continue_exit_process(Process *p)
dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL;
scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
+ if (scb)
+ p->fcalls += CONTEXT_REDS; /* Reduction counting depends on this... */
pbt = ERTS_PROC_SET_CALL_TIME(p, NULL);
nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 61acf5924b..12a919bc87 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -805,8 +805,15 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
#define ERTS_PSD_CALL_TIME_BP 3
#define ERTS_PSD_DELAYED_GC_TASK_QS 4
#define ERTS_PSD_NIF_TRAP_EXPORT 5
+#ifdef HIPE
+#define ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF 6
+#endif
+#ifdef HIPE
+#define ERTS_PSD_SIZE 7
+#else
#define ERTS_PSD_SIZE 6
+#endif
typedef struct {
void *data[ERTS_PSD_SIZE];
@@ -911,6 +918,15 @@ struct ErtsPendingSuspend_ {
# define BIN_OLD_VHEAP_SZ(p) (p)->bin_old_vheap_sz
# define BIN_OLD_VHEAP(p) (p)->bin_old_vheap
+# define MAX_HEAP_SIZE_GET(p) ((p)->max_heap_size >> 2)
+# define MAX_HEAP_SIZE_SET(p, sz) ((p)->max_heap_size = ((sz) << 2) | \
+ MAX_HEAP_SIZE_FLAGS_GET(p))
+# define MAX_HEAP_SIZE_FLAGS_GET(p) ((p)->max_heap_size & 0x3)
+# define MAX_HEAP_SIZE_FLAGS_SET(p, flags) ((p)->max_heap_size = flags | \
+ ((p)->max_heap_size & ~0x3))
+# define MAX_HEAP_SIZE_KILL 1
+# define MAX_HEAP_SIZE_LOG 2
+
struct process {
ErtsPTabElementCommon common; /* *Need* to be first in struct */
@@ -925,10 +941,10 @@ struct process {
Eterm* stop; /* Stack top */
Eterm* heap; /* Heap start */
Eterm* hend; /* Heap end */
- Eterm* abandoned_heap;
Uint heap_sz; /* Size of heap in words */
Uint min_heap_size; /* Minimum size of heap (in words). */
Uint min_vheap_size; /* Minimum size of virtual heap (in words). */
+ Uint max_heap_size; /* Maximum size of heap (in words). */
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
volatile unsigned long fp_exception;
@@ -941,6 +957,16 @@ struct process {
#endif
/*
+ * Moved to after "struct hipe_process_state hipe", as a temporary fix for
+ * LLVM hard-coding offsetof(struct process, hipe.nstack) (sic!)
+ * (see void X86FrameLowering::adjustForHiPEPrologue(...) in
+ * lib/Target/X86/X86FrameLowering.cpp).
+ *
+ * Used to be below "Eterm* hend".
+ */
+ Eterm* abandoned_heap;
+
+ /*
* Saved x registers.
*/
Uint arity; /* Number of live argument registers (only valid
@@ -1071,6 +1097,10 @@ struct process {
Uint space_verified; /* Avoid HAlloc forcing heap fragments when */
Eterm* space_verified_from; /* we rely on available heap space (TestHeap) */
#endif
+
+#ifdef DEBUG
+ Uint debug_reds_in;
+#endif
};
extern const Process erts_invalid_process;
@@ -1265,6 +1295,8 @@ typedef struct {
Uint min_vheap_size; /* Minimum virtual heap size */
int priority; /* Priority for process. */
Uint16 max_gen_gcs; /* Maximum number of gen GCs before fullsweep. */
+ Uint max_heap_size; /* Maximum heap size in words */
+ Uint max_heap_flags; /* Maximum heap flags (kill | log) */
int scheduler;
} ErlSpawnOpts;
@@ -1342,6 +1374,7 @@ extern int erts_system_profile_ts_type;
#define F_DELAY_GC (1 << 16) /* Similar to disable GC (see below) */
#define F_SCHDLR_ONLN_WAITQ (1 << 17) /* Process enqueued waiting to change schedulers online */
#define F_HAVE_BLCKD_NMSCHED (1 << 18) /* Process has blocked normal multi-scheduling */
+#define F_HIPE_MODE (1 << 19)
/*
* F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent
@@ -1999,6 +2032,13 @@ erts_psd_set(Process *p, int ix, void *data)
#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \
erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE))
+#ifdef HIPE
+#define ERTS_PROC_GET_SUSPENDED_SAVED_CALLS_BUF(P) \
+ ((struct saved_calls *) erts_psd_get((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF))
+#define ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(P, SCB) \
+ ((struct saved_calls *) erts_psd_set((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF, (void *) (SCB)))
+#endif
+
ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p);
ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, Eterm handler);
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 346404fe2a..fadbd704bd 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1966,7 +1966,7 @@ send_time_offset_changed_notifications(void *new_offsetp)
*patch_refp = ref;
ASSERT(hsz == size_object(message_template));
message = copy_struct(message_template, hsz, &hp, ohp);
- erts_queue_message(rp, &rp_locks, mp, message);
+ erts_queue_message(rp, rp_locks, mp, message, am_clock_service);
}
erts_smp_proc_unlock(rp, rp_locks);
}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index bd88769dfc..6d6373b706 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -237,7 +237,7 @@ write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp)
}
}
-#define PATCH_TS_SIZE(p) patch_ts_size(TFLGS_TS_TYPE(p))
+#ifdef ERTS_SMP
static ERTS_INLINE Uint
patch_ts_size(int ts_type)
@@ -257,6 +257,7 @@ patch_ts_size(int ts_type)
return 0;
}
}
+#endif /* ERTS_SMP */
/*
* Write a timestamp. The timestamp MUST be the last
@@ -359,25 +360,51 @@ void erts_init_trace(void) {
*(OHPP) = &(*(BPP))->off_heap, \
(*(BPP))->mem)
+enum ErtsTracerOpt {
+ TRACE_FUN_DEFAULT = 0,
+ TRACE_FUN_ENABLED = 1,
+ TRACE_FUN_T_SEND = 2,
+ TRACE_FUN_T_RECEIVE = 3,
+ TRACE_FUN_T_CALL = 4,
+ TRACE_FUN_T_SCHED_PROC = 5,
+ TRACE_FUN_T_SCHED_PORT = 6,
+ TRACE_FUN_T_GC = 7,
+ TRACE_FUN_T_PROCS = 8,
+ TRACE_FUN_T_PORTS = 9,
+ TRACE_FUN_E_SEND = 10,
+ TRACE_FUN_E_RECEIVE = 11,
+ TRACE_FUN_E_CALL = 12,
+ TRACE_FUN_E_SCHED_PROC = 13,
+ TRACE_FUN_E_SCHED_PORT = 14,
+ TRACE_FUN_E_GC = 15,
+ TRACE_FUN_E_PROCS = 16,
+ TRACE_FUN_E_PORTS = 17
+};
+
+#define NIF_TRACER_TYPES (18)
+
+
static ERTS_INLINE int
send_to_tracer_nif_raw(Process *c_p, Process *tracee, const ErtsTracer tracer,
Uint trace_flags, Eterm t_p_id, ErtsTracerNif *tnif,
+ enum ErtsTracerOpt topt,
Eterm tag, Eterm msg, Eterm extra, Eterm pam_result);
static ERTS_INLINE int
send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
- Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag,
- Eterm msg, Eterm extra);
+ Eterm t_p_id, ErtsTracerNif *tnif,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm msg, Eterm extra,
+ Eterm pam_result);
static ERTS_INLINE Eterm
call_enabled_tracer(Process *c_p, const ErtsTracer tracer,
- ErtsTracerNif **tnif_ref, Eterm tag, Eterm t_p_id);
+ ErtsTracerNif **tnif_ref,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm t_p_id);
static int
-is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks,
- ErtsPTabElementCommon *t_p,
- ErtsTracerNif **tnif_ret, Eterm tag);
-
-#define SEND_TO_TRACER(c_p, tag, msg) \
- send_to_tracer_nif(c_p, &(c_p)->common, (c_p)->common.id, NULL, tag, \
- msg, THE_NON_VALUE)
+is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p,
+ ErtsTracerNif **tnif_ret,
+ enum ErtsTracerOpt topt, Eterm tag);
static Uint active_sched;
@@ -433,7 +460,7 @@ erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, ErtsTracer new
if (!ERTS_TRACER_IS_NIL(new)) {
Eterm nif_result = call_enabled_tracer(
NULL, new, NULL,
- am_trace_status, am_undefined);
+ TRACE_FUN_ENABLED, am_trace_status, am_undefined);
switch (nif_result) {
case am_trace: break;
default:
@@ -465,7 +492,8 @@ erts_get_system_seq_tracer(void)
erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
if (st != erts_tracer_nil &&
- call_enabled_tracer(NULL, st, NULL, am_trace_status, am_undefined) == am_remove) {
+ call_enabled_tracer(NULL, st, NULL, TRACE_FUN_ENABLED,
+ am_trace_status, am_undefined) == am_remove) {
erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil);
st = erts_tracer_nil;
}
@@ -484,10 +512,11 @@ get_default_tracing(Uint *flagsp, ErtsTracer *tracerp,
if (ERTS_TRACER_IS_NIL(*default_tracer)) {
*default_trace_flags &= ~TRACEE_FLAGS;
} else {
- Eterm nif_result = call_enabled_tracer(
- NULL, *default_tracer, NULL,
- am_trace_status, am_undefined);
- switch (nif_result) {
+ Eterm nif_res;
+ nif_res = call_enabled_tracer(NULL, *default_tracer,
+ NULL, TRACE_FUN_ENABLED,
+ am_trace_status, am_undefined);
+ switch (nif_res) {
case am_trace: break;
default: {
ErtsTracer curr_default_tracer = *default_tracer;
@@ -629,6 +658,7 @@ do { \
# define GET_NOW(m, s, u) do {get_now(m, s, u);} while (0)
#endif
+
static void
write_sys_msg_to_port(Eterm unused_to,
Port* trace_port,
@@ -709,7 +739,7 @@ profile_send(Eterm from, Eterm message) {
else
msg = copy_struct(message, sz, &hp, &mp->hfrag.off_heap);
- erts_queue_message(profile_p, NULL, mp, msg);
+ erts_queue_message(profile_p, 0, mp, msg, from);
}
}
@@ -737,7 +767,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what)
break;
}
- if (!is_tracer_proc_enabled(p, locks, &p->common, &tnif, what))
+ if (!is_tracer_enabled(p, locks, &p->common, &tnif, TRACE_FUN_E_SCHED_PROC, what))
return;
if (ERTS_PROC_IS_EXITING(p))
@@ -756,8 +786,8 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what)
hp += 4;
}
- send_to_tracer_nif(p, &p->common, p->common.id, tnif,
- what, tmp, THE_NON_VALUE);
+ send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SCHED_PROC,
+ what, tmp, THE_NON_VALUE, am_true);
}
/* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp}
@@ -782,9 +812,32 @@ trace_send(Process *p, Eterm to, Eterm msg)
{
Eterm operation = am_send;
ErtsTracerNif *tnif = NULL;
+ ErtsTracingEvent* te;
+ Eterm pam_result;
ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND));
+ te = &erts_send_tracing[erts_active_bp_ix()];
+ if (!te->on) {
+ return;
+ }
+ if (te->match_spec) {
+ Eterm args[2];
+ Uint32 return_flags;
+ args[0] = to;
+ args[1] = msg;
+ pam_result = erts_match_set_run_trace(p, p,
+ te->match_spec, args, 2,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ if (pam_result == am_false)
+ return;
+ if (ERTS_TRACE_FLAGS(p) & F_TRACE_SILENT) {
+ erts_match_set_release_result_trace(p, pam_result);
+ return;
+ }
+ } else
+ pam_result = am_true;
+
if (is_internal_pid(to)) {
if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
@@ -795,21 +848,64 @@ trace_send(Process *p, Eterm to, Eterm msg)
operation = am_send_to_non_existing_process;
}
- if (is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, operation))
- send_to_tracer_nif(p, &p->common, p->common.id, tnif, operation, msg, to);
+ if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
+ TRACE_FUN_E_SEND, operation)) {
+ send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND,
+ operation, msg, to, pam_result);
+ }
+ erts_match_set_release_result_trace(p, pam_result);
}
/* Send {trace_ts, Pid, receive, Msg, Timestamp}
* or {trace, Pid, receive, Msg}
*/
void
-trace_receive(Process *c_p, Eterm msg)
+trace_receive(Process* receiver,
+ Eterm from,
+ Eterm msg, ErtsTracingEvent* te)
{
ErtsTracerNif *tnif = NULL;
- if (is_tracer_proc_enabled(NULL, 0, &c_p->common,
- &tnif, am_receive))
- send_to_tracer_nif(NULL, &c_p->common, c_p->common.id,
- tnif, am_receive, msg, THE_NON_VALUE);
+ Eterm pam_result;
+
+ if (!te) {
+ te = &erts_receive_tracing[erts_active_bp_ix()];
+ if (!te->on)
+ return;
+ }
+ else ASSERT(te->on);
+
+ if (te->match_spec) {
+ Eterm args[3];
+ Uint32 return_flags;
+ if (is_pid(from)) {
+ args[0] = pid_node_name(from);
+ args[1] = from;
+ }
+ else {
+ ASSERT(is_atom(from));
+ args[0] = from; /* node name or other atom (e.g 'system') */
+ args[1] = am_undefined;
+ }
+ args[2] = msg;
+ pam_result = erts_match_set_run_trace(NULL, receiver,
+ te->match_spec, args, 3,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ if (pam_result == am_false)
+ return;
+ if (ERTS_TRACE_FLAGS(receiver) & F_TRACE_SILENT) {
+ erts_match_set_release_result_trace(NULL, pam_result);
+ return;
+ }
+ } else
+ pam_result = am_true;
+
+ if (is_tracer_enabled(NULL, 0, &receiver->common, &tnif,
+ TRACE_FUN_E_RECEIVE, am_receive)) {
+ send_to_tracer_nif(NULL, &receiver->common, receiver->common.id,
+ tnif, TRACE_FUN_T_RECEIVE,
+ am_receive, msg, THE_NON_VALUE, pam_result);
+ }
+ erts_match_set_release_result_trace(NULL, pam_result);
}
int
@@ -819,8 +915,9 @@ seq_trace_update_send(Process *p)
ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p))));
if (have_no_seqtrace(SEQ_TRACE_TOKEN(p)) ||
(seq_tracer != NIL &&
- call_enabled_tracer( NULL, seq_tracer, NULL, am_trace_status,
- p ? p->common.id : am_undefined) != am_trace)
+ call_enabled_tracer(NULL, seq_tracer, NULL,
+ TRACE_FUN_ENABLED, am_trace_status,
+ p ? p->common.id : am_undefined) != am_trace)
#ifdef USE_VM_PROBES
|| (SEQ_TRACE_TOKEN(p) == am_have_dt_utag)
#endif
@@ -866,9 +963,10 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
ASSERT(is_tuple(token) || is_nil(token));
if (token == NIL || (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE) ||
ERTS_TRACER_IS_NIL(seq_tracer) ||
- call_enabled_tracer(
- NULL, seq_tracer, NULL, am_trace_status,
- process ? process->common.id : am_undefined) != am_trace) {
+ call_enabled_tracer(NULL, seq_tracer,
+ NULL, TRACE_FUN_ENABLED,
+ am_trace_status,
+ process ? process->common.id : am_undefined) != am_trace) {
return;
}
@@ -897,14 +995,13 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
msg = TUPLE3(hp, am_EXIT, exitfrom, msg);
hp += 4;
}
- mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token),
- receiver, msg);
+ mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token), receiver, msg);
hp += 6;
seq_tracer_flags |= ERTS_SEQTFLGS2TFLGS(unsigned_val(SEQ_TRACE_T_FLAGS(token)));
send_to_tracer_nif_raw(NULL, process, seq_tracer, seq_tracer_flags,
- label, NULL, am_seq_trace, mess,
+ label, NULL, TRACE_FUN_DEFAULT, am_seq_trace, mess,
THE_NON_VALUE, am_true);
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
@@ -929,7 +1026,8 @@ erts_trace_return_to(Process *p, BeamInstr *pc)
mfa = TUPLE3(hp, code_ptr[0], code_ptr[1], make_small(code_ptr[2]));
}
- SEND_TO_TRACER(p, am_return_to, mfa);
+ send_to_tracer_nif(p, &p->common, p->common.id, NULL, TRACE_FUN_T_CALL,
+ am_return_to, mfa, THE_NON_VALUE, am_true);
}
@@ -983,7 +1081,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, ErtsTracer *tracer)
mfa = TUPLE3(hp, mod, name, make_small(arity));
hp += 4;
send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id,
- NULL, am_return_from, mfa, retval, am_true);
+ NULL, TRACE_FUN_T_CALL, am_return_from, mfa, retval, am_true);
}
/* Send {trace_ts, Pid, exception_from, {Mod, Name, Arity}, {Class,Value},
@@ -1038,7 +1136,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value,
cv = TUPLE2(hp, class, value);
hp += 3;
send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id,
- NULL, am_exception_from, mfa_tuple, cv, am_true);
+ NULL, TRACE_FUN_T_CALL, am_exception_from, mfa_tuple, cv, am_true);
}
/*
@@ -1088,7 +1186,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* use process flags
*/
tracee_flags = &ERTS_TRACE_FLAGS(p);
- if (!is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, am_call)) {
+ if (!is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
+ TRACE_FUN_E_CALL, am_call)) {
return 0;
}
} else {
@@ -1103,7 +1202,9 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
}
meta_flags = F_TRACE_CALLS | F_NOW_TS;
tracee_flags = &meta_flags;
- switch (call_enabled_tracer(p, *tracer, &tnif, am_call, p->common.id)) {
+ switch (call_enabled_tracer(p, *tracer,
+ &tnif, TRACE_FUN_T_CALL,
+ am_call, p->common.id)) {
default:
case am_remove: *tracer = erts_tracer_nil;
case am_discard: return 0;
@@ -1164,20 +1265,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
may remove it, and we still want to generate a trace message */
erts_tracer_update(&pre_ms_tracer, *tracer);
tracer = &pre_ms_tracer;
- pam_result = erts_match_set_run(p, match_spec, args, arity,
- ERTS_PAM_TMP_RESULT, &return_flags);
- if (is_non_value(pam_result)) {
- erts_match_set_release_result(p);
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- ERTS_TRACER_CLEAR(&pre_ms_tracer);
- return 0;
- }
+ pam_result = erts_match_set_run_trace(p, p,
+ match_spec, args, arity,
+ ERTS_PAM_TMP_RESULT, &return_flags);
}
if (tracee_flags == &meta_flags) {
/* Meta trace */
if (pam_result == am_false) {
- erts_match_set_release_result(p);
UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
ERTS_TRACER_CLEAR(&pre_ms_tracer);
return return_flags;
@@ -1185,13 +1280,12 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
} else {
/* Non-meta trace */
if (*tracee_flags & F_TRACE_SILENT) {
- erts_match_set_release_result(p);
+ erts_match_set_release_result_trace(p, pam_result);
UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
ERTS_TRACER_CLEAR(&pre_ms_tracer);
return 0;
}
if (pam_result == am_false) {
- erts_match_set_release_result(p);
UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
ERTS_TRACER_CLEAR(&pre_ms_tracer);
return return_flags;
@@ -1227,11 +1321,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* Build the trace tuple and send it to the port.
*/
send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id,
- tnif, am_call, mfa_tuple, THE_NON_VALUE, pam_result);
- erts_match_set_release_result(p);
+ tnif, TRACE_FUN_T_CALL, am_call, mfa_tuple,
+ THE_NON_VALUE, pam_result);
- if (match_spec && tracer == &pre_ms_tracer)
- ERTS_TRACER_CLEAR(&pre_ms_tracer);
+ if (match_spec) {
+ erts_match_set_release_result_trace(p, pam_result);
+ if (tracer == &pre_ms_tracer)
+ ERTS_TRACER_CLEAR(&pre_ms_tracer);
+ }
return return_flags;
}
@@ -1249,9 +1346,10 @@ trace_proc(Process *c_p, ErtsProcLocks c_p_locks,
Process *t_p, Eterm what, Eterm data)
{
ErtsTracerNif *tnif = NULL;
- if (is_tracer_proc_enabled(c_p, c_p_locks, &t_p->common, &tnif, what))
- send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif,
- what, data, THE_NON_VALUE);
+ if (is_tracer_enabled(c_p, c_p_locks, &t_p->common, &tnif,
+ TRACE_FUN_E_PROCS, what))
+ send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PROCS,
+ what, data, THE_NON_VALUE, am_true);
}
@@ -1267,31 +1365,34 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid,
Eterm mod, Eterm func, Eterm args)
{
ErtsTracerNif *tnif = NULL;
- if (is_tracer_proc_enabled(p, ERTS_PROC_LOCKS_ALL &
+ if (is_tracer_enabled(p, ERTS_PROC_LOCKS_ALL &
~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE),
- &p->common, &tnif, what)) {
+ &p->common, &tnif, TRACE_FUN_E_PROCS, what)) {
Eterm mfa;
Eterm* hp;
hp = HAlloc(p, 4);
mfa = TUPLE3(hp, mod, func, args);
hp += 4;
- send_to_tracer_nif(p, &p->common, p->common.id, tnif, what, pid, mfa);
+ send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_PROCS,
+ what, pid, mfa, am_true);
}
}
void save_calls(Process *p, Export *e)
{
- struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
- if (scb) {
- Export **ct = &scb->ct[0];
- int len = scb->len;
-
- ct[scb->cur] = e;
- if (++scb->cur >= len)
- scb->cur = 0;
- if (scb->n < len)
- scb->n++;
+ if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
+ struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
+ if (scb) {
+ Export **ct = &scb->ct[0];
+ int len = scb->len;
+
+ ct[scb->cur] = e;
+ if (++scb->cur >= len)
+ scb->cur = 0;
+ if (scb->n < len)
+ scb->n++;
+ }
}
}
@@ -1308,23 +1409,28 @@ void save_calls(Process *p, Export *e)
* are all small (atomic) integers.
*/
void
-trace_gc(Process *p, Eterm what)
+trace_gc(Process *p, Eterm what, Uint size, Eterm msg)
{
ErtsTracerNif *tnif = NULL;
Eterm* hp;
- Eterm msg = NIL;
- Uint size = 0;
+ Uint sz = 0;
+ Eterm tup;
- if (is_tracer_proc_enabled(
- p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, what)) {
+ if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
+ TRACE_FUN_E_GC, what)) {
- (void) erts_process_gc_info(p, &size, NULL);
- hp = HAlloc(p, size);
+ if (is_non_value(msg)) {
- msg = erts_process_gc_info(p, NULL, &hp);
+ (void) erts_process_gc_info(p, &sz, NULL, 0, 0);
+ hp = HAlloc(p, sz + 3 + 2);
- send_to_tracer_nif(p, &p->common, p->common.id, tnif, what,
- msg, THE_NON_VALUE);
+ msg = erts_process_gc_info(p, NULL, &hp, 0, 0);
+ tup = TUPLE2(hp, am_wordsize, make_small(size)); hp += 3;
+ msg = CONS(hp, tup, msg); hp += 2;
+ }
+
+ send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_GC,
+ what, msg, THE_NON_VALUE, am_true);
}
}
@@ -1387,7 +1493,7 @@ monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1452,7 +1558,7 @@ monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time)
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1527,7 +1633,7 @@ monitor_long_gc(Process *p, Uint time) {
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1602,7 +1708,7 @@ monitor_large_heap(Process *p) {
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1634,7 +1740,7 @@ monitor_generic(Process *p, Eterm type, Eterm spec) {
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
@@ -1696,50 +1802,15 @@ profile_scheduler(Eterm scheduler_id, Eterm state) {
}
-void
-profile_scheduler_q(Eterm scheduler_id, Eterm state, Eterm no_schedulers, Uint Ms, Uint s, Uint us) {
- Eterm *hp, msg, timestamp;
-
-#ifndef ERTS_SMP
-#define LOCAL_HEAP_SIZE (4 + 7)
- DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
- UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-
- hp = local_heap;
-#else
- ErlHeapFragment *bp;
- Uint hsz;
-
- hsz = 4 + 7;
-
- bp = new_message_buffer(hsz);
- hp = bp->mem;
-#endif
-
- erts_smp_mtx_lock(&smq_mtx);
-
- timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4;
- msg = TUPLE6(hp, am_profile, am_scheduler, scheduler_id, state, no_schedulers, timestamp); hp += 7;
-#ifndef ERTS_SMP
- profile_send(NIL, msg);
- UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
-#undef LOCAL_HEAP_SIZE
-#else
- enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SYSPROF, NIL, NIL, msg, bp);
-#endif
- erts_smp_mtx_unlock(&smq_mtx);
-
-}
-
/* Port profiling */
void
trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) {
ErtsTracerNif *tnif = NULL;
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (is_tracer_proc_enabled(NULL, 0, &p->common, &tnif, am_open))
- send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, am_open,
- calling_pid, drv_name);
+ if (is_tracer_enabled(NULL, 0, &p->common, &tnif, TRACE_FUN_E_PORTS, am_open))
+ send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_T_PORTS,
+ am_open, calling_pid, drv_name, am_true);
}
/* Sends trace message:
@@ -1756,9 +1827,9 @@ trace_port(Port *t_p, Eterm what, Eterm data) {
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
|| erts_thr_progress_is_blocking());
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what))
- send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif,
- what, data, THE_NON_VALUE);
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_PORTS, what))
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PORTS,
+ what, data, THE_NON_VALUE, am_true);
}
@@ -1802,7 +1873,7 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
|| erts_thr_progress_is_blocking());
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, am_receive)) {
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_RECEIVE, am_receive)) {
/* We can use a stack heap here, as the nif is called in the
context of a port */
#define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT) + 3)
@@ -1894,8 +1965,11 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...)
}
data = TUPLE2(hp, caller, data);
+ hp += 3;
+ ASSERT(hp <= (local_heap + LOCAL_HEAP_SIZE) || orig_hp);
send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif,
- am_receive, data, THE_NON_VALUE);
+ TRACE_FUN_T_RECEIVE,
+ am_receive, data, THE_NON_VALUE, am_true);
if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0)
erts_bin_free(bptr);
@@ -1916,9 +1990,9 @@ trace_port_send(Port *t_p, Eterm receiver, Eterm msg, int exists)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
|| erts_thr_progress_is_blocking());
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, op))
- send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif,
- op, msg, receiver);
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, op))
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND,
+ op, msg, receiver, am_true);
}
void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz)
@@ -1927,7 +2001,7 @@ void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
|| erts_thr_progress_is_blocking());
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, am_send)) {
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, am_send)) {
Eterm msg;
Binary* bptr = NULL;
#define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT))
@@ -1946,8 +2020,8 @@ void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz)
msg = TUPLE2(hp, t_p->common.id, msg);
hp += 3;
- send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif,
- am_send, msg, to);
+ send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND,
+ am_send, msg, to, am_true);
if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0)
erts_bin_free(bptr);
@@ -1975,9 +2049,10 @@ trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) {
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
|| erts_thr_progress_is_blocking());
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what))
+ if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SCHED_PORT, what))
send_to_tracer_nif(NULL, &t_p->common, t_p->common.id,
- tnif, what, where, THE_NON_VALUE);
+ tnif, TRACE_FUN_T_SCHED_PORT,
+ what, where, THE_NON_VALUE, am_true);
}
/* Port profiling */
@@ -2417,7 +2492,7 @@ sys_msg_dispatcher_func(void *unused)
queue_proc_msg:
mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = smqp->bp;
- erts_queue_message(proc,&proc_locks,mp,smqp->msg);
+ erts_queue_message(proc,proc_locks,mp,smqp->msg,am_system);
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "delivered\n");
#endif
@@ -2523,14 +2598,97 @@ init_sys_msg_dispatcher(void)
#include "erl_nif.h"
+typedef struct {
+ char *name;
+ Uint arity;
+ ErlNifFunc *cb;
+} ErtsTracerType;
+
struct ErtsTracerNif_ {
HashBucket hb;
Eterm module;
struct erl_module_nif* nif_mod;
- ErlNifFunc *enabled;
- ErlNifFunc *trace;
+ ErtsTracerType tracers[NIF_TRACER_TYPES];
};
+static void init_tracer_template(ErtsTracerNif *tnif) {
+
+ /* default tracer functions */
+ tnif->tracers[TRACE_FUN_DEFAULT].name = "trace";
+ tnif->tracers[TRACE_FUN_DEFAULT].arity = 6;
+ tnif->tracers[TRACE_FUN_DEFAULT].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_ENABLED].name = "enabled";
+ tnif->tracers[TRACE_FUN_ENABLED].arity = 3;
+ tnif->tracers[TRACE_FUN_ENABLED].cb = NULL;
+
+ /* specific tracer functions */
+ tnif->tracers[TRACE_FUN_T_SEND].name = "trace_send";
+ tnif->tracers[TRACE_FUN_T_SEND].arity = 6;
+ tnif->tracers[TRACE_FUN_T_SEND].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_RECEIVE].name = "trace_receive";
+ tnif->tracers[TRACE_FUN_T_RECEIVE].arity = 6;
+ tnif->tracers[TRACE_FUN_T_RECEIVE].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_CALL].name = "trace_call";
+ tnif->tracers[TRACE_FUN_T_CALL].arity = 6;
+ tnif->tracers[TRACE_FUN_T_CALL].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_SCHED_PROC].name = "trace_running_procs";
+ tnif->tracers[TRACE_FUN_T_SCHED_PROC].arity = 6;
+ tnif->tracers[TRACE_FUN_T_SCHED_PROC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_SCHED_PORT].name = "trace_running_ports";
+ tnif->tracers[TRACE_FUN_T_SCHED_PORT].arity = 6;
+ tnif->tracers[TRACE_FUN_T_SCHED_PORT].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_GC].name = "trace_garbage_collection";
+ tnif->tracers[TRACE_FUN_T_GC].arity = 6;
+ tnif->tracers[TRACE_FUN_T_GC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_PROCS].name = "trace_procs";
+ tnif->tracers[TRACE_FUN_T_PROCS].arity = 6;
+ tnif->tracers[TRACE_FUN_T_PROCS].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_T_PORTS].name = "trace_ports";
+ tnif->tracers[TRACE_FUN_T_PORTS].arity = 6;
+ tnif->tracers[TRACE_FUN_T_PORTS].cb = NULL;
+
+ /* specific enabled functions */
+ tnif->tracers[TRACE_FUN_E_SEND].name = "enabled_send";
+ tnif->tracers[TRACE_FUN_E_SEND].arity = 3;
+ tnif->tracers[TRACE_FUN_E_SEND].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_RECEIVE].name = "enabled_receive";
+ tnif->tracers[TRACE_FUN_E_RECEIVE].arity = 3;
+ tnif->tracers[TRACE_FUN_E_RECEIVE].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_CALL].name = "enabled_call";
+ tnif->tracers[TRACE_FUN_E_CALL].arity = 3;
+ tnif->tracers[TRACE_FUN_E_CALL].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_SCHED_PROC].name = "enabled_running_procs";
+ tnif->tracers[TRACE_FUN_E_SCHED_PROC].arity = 3;
+ tnif->tracers[TRACE_FUN_E_SCHED_PROC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_SCHED_PORT].name = "enabled_running_ports";
+ tnif->tracers[TRACE_FUN_E_SCHED_PORT].arity = 3;
+ tnif->tracers[TRACE_FUN_E_SCHED_PORT].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_GC].name = "enabled_garbage_collection";
+ tnif->tracers[TRACE_FUN_E_GC].arity = 3;
+ tnif->tracers[TRACE_FUN_E_GC].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_PROCS].name = "enabled_procs";
+ tnif->tracers[TRACE_FUN_E_PROCS].arity = 3;
+ tnif->tracers[TRACE_FUN_E_PROCS].cb = NULL;
+
+ tnif->tracers[TRACE_FUN_E_PORTS].name = "enabled_ports";
+ tnif->tracers[TRACE_FUN_E_PORTS].arity = 3;
+ tnif->tracers[TRACE_FUN_E_PORTS].cb = NULL;
+}
+
static Hash *tracer_hash = NULL;
static erts_smp_rwmtx_t tracer_mtx;
@@ -2543,31 +2701,32 @@ load_tracer_nif(const ErtsTracer tracer)
ErlNifFunc *funcs;
int num_of_funcs;
ErtsTracerNif tnif_tmpl, *tnif;
- int i;
+ ErtsTracerType *tracers;
+ int i,j;
- if (mod && mod->curr.nif != NULL) {
- instance = &mod->curr;
- } else {
+ if (!mod || !mod->curr.nif) {
return NULL;
}
- tnif_tmpl.enabled = NULL;
- tnif_tmpl.trace = NULL;
+ instance = &mod->curr;
+
+ init_tracer_template(&tnif_tmpl);
tnif_tmpl.nif_mod = instance->nif;
tnif_tmpl.module = ERTS_TRACER_MODULE(tracer);
+ tracers = tnif_tmpl.tracers;
num_of_funcs = erts_nif_get_funcs(instance->nif, &funcs);
for(i = 0; i < num_of_funcs; i++) {
- if (strcmp("enabled",funcs[i].name) == 0 && funcs[i].arity == 3) {
- tnif_tmpl.enabled = funcs + i;
- } else if (strcmp("trace",funcs[i].name) == 0 && funcs[i].arity == 6) {
- tnif_tmpl.trace = funcs + i;
+ for (j = 0; j < NIF_TRACER_TYPES; j++) {
+ if (strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) {
+ tracers[j].cb = &(funcs[i]);
+ break;
+ }
}
}
- if (tnif_tmpl.enabled == NULL ||
- tnif_tmpl.trace == NULL ) {
+ if (tracers[TRACE_FUN_DEFAULT].cb == NULL || tracers[TRACE_FUN_ENABLED].cb == NULL ) {
return NULL;
}
@@ -2660,17 +2819,18 @@ erts_tracer_to_term(Process *p, ErtsTracer tracer)
static ERTS_INLINE int
send_to_tracer_nif_raw(Process *c_p, Process *tracee,
const ErtsTracer tracer, Uint tracee_flags,
- Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag, Eterm msg,
- Eterm extra, Eterm pam_result)
+ Eterm t_p_id, ErtsTracerNif *tnif,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm msg, Eterm extra, Eterm pam_result)
{
if (tnif || (tnif = lookup_tracer_nif(tracer)) != NULL) {
#define MAP_SIZE 3
- Eterm argv[6],
- local_heap[3+MAP_SIZE /* values */+(MAP_SIZE+1 /* keys */)];
+ Eterm argv[6], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)];
flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1));
Eterm *map_values = flatmap_get_values(map);
- int argc = 6;
+ topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_DEFAULT;
+ ASSERT(topt < NIF_TRACER_TYPES);
argv[0] = tag;
argv[1] = ERTS_TRACER_STATE(tracer);
@@ -2681,8 +2841,7 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee,
map->thing_word = MAP_HEADER_FLATMAP;
map->size = MAP_SIZE;
- map->keys = TUPLE3(local_heap, am_match_spec_result, am_scheduler_id,
- am_timestamp);
+ map->keys = TUPLE3(local_heap, am_match_spec_result, am_scheduler_id, am_timestamp);
*map_values++ = pam_result;
if (tracee_flags & F_TRACE_SCHED_NO)
@@ -2705,16 +2864,18 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee,
#undef MAP_SIZE
erts_nif_call_function(c_p, tracee ? tracee : c_p,
- tnif->nif_mod, tnif->trace, argc, argv);
+ tnif->nif_mod,
+ tnif->tracers[topt].cb,
+ tnif->tracers[topt].arity,
+ argv);
}
return 1;
}
-
static ERTS_INLINE int
send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
- Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag,
- Eterm msg, Eterm extra)
+ Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt topt,
+ Eterm tag, Eterm msg, Eterm extra, Eterm pam_result)
{
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
if (c_p) {
@@ -2733,29 +2894,35 @@ send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
return send_to_tracer_nif_raw(c_p,
is_internal_pid(t_p->id) ? (Process*)t_p : NULL,
t_p->tracer, t_p->trace_flags,
- t_p_id, tnif, tag, msg, extra,
- am_true);
+ t_p_id, tnif, topt, tag, msg, extra,
+ pam_result);
}
static ERTS_INLINE Eterm
call_enabled_tracer(Process *c_p, const ErtsTracer tracer,
- ErtsTracerNif **tnif_ret, Eterm tag, Eterm t_p_id)
-{
+ ErtsTracerNif **tnif_ret,
+ enum ErtsTracerOpt topt,
+ Eterm tag, Eterm t_p_id) {
ErtsTracerNif *tnif = lookup_tracer_nif(tracer);
if (tnif) {
Eterm argv[] = {tag, ERTS_TRACER_STATE(tracer), t_p_id};
+ topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_ENABLED;
+ ASSERT(topt < NIF_TRACER_TYPES);
+ ASSERT(tnif->tracers[topt].cb != NULL);
if (tnif_ret) *tnif_ret = tnif;
- return erts_nif_call_function(
- c_p, NULL, tnif->nif_mod, tnif->enabled, 3, argv);
+ return erts_nif_call_function(c_p, NULL, tnif->nif_mod,
+ tnif->tracers[topt].cb,
+ tnif->tracers[topt].arity,
+ argv);
}
return am_remove;
}
static int
-is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks,
- ErtsPTabElementCommon *t_p,
- ErtsTracerNif **tnif_ret, Eterm tag)
-{
+is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
+ ErtsPTabElementCommon *t_p,
+ ErtsTracerNif **tnif_ret,
+ enum ErtsTracerOpt topt, Eterm tag) {
Eterm nif_result;
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
@@ -2773,7 +2940,7 @@ is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks,
}
#endif
- nif_result = call_enabled_tracer(c_p, t_p->tracer, tnif_ret, tag, t_p->id);
+ nif_result = call_enabled_tracer(c_p, t_p->tracer, tnif_ret, topt, tag, t_p->id);
switch (nif_result) {
case am_discard: return 0;
case am_trace: return 1;
@@ -2806,14 +2973,13 @@ is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks,
erts_smp_proc_unlock(c_p, c_p_xlocks);
}
-
return 0;
}
int erts_is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks,
ErtsPTabElementCommon *t_p, Eterm type)
{
- return is_tracer_proc_enabled(c_p, c_p_locks, t_p, NULL, am_trace_status);
+ return is_tracer_enabled(c_p, c_p_locks, t_p, NULL, TRACE_FUN_ENABLED, am_trace_status);
}
int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer)
@@ -2821,7 +2987,7 @@ int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer)
ErtsTracerNif *tnif = lookup_tracer_nif(tracer);
if (tnif) {
Eterm nif_result = call_enabled_tracer(c_p, tracer, &tnif,
- am_trace_status,
+ TRACE_FUN_ENABLED, am_trace_status,
c_p->common.id);
switch (nif_result) {
case am_discard:
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index 177fd373a6..fd5879bc9d 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -56,6 +56,15 @@
struct binary;
+typedef struct
+{
+ int on;
+ struct binary* match_spec;
+} ErtsTracingEvent;
+
+extern ErtsTracingEvent erts_send_tracing[];
+extern ErtsTracingEvent erts_receive_tracing[];
+
/* erl_bif_trace.c */
Eterm erl_seq_trace_info(Process *p, Eterm arg1);
void erts_system_monitor_clear(Process *c_p);
@@ -91,7 +100,7 @@ void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *);
#endif
void trace_send(Process*, Eterm, Eterm);
-void trace_receive(Process *, Eterm);
+void trace_receive(Process*, Eterm, Eterm, ErtsTracingEvent*);
Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec,
Eterm* args, int local, ErtsTracer *tracer);
void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval,
@@ -103,7 +112,7 @@ void trace_sched(Process*, ErtsProcLocks, Eterm);
void trace_proc(Process*, ErtsProcLocks, Process*, Eterm, Eterm);
void trace_proc_spawn(Process*, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args);
void save_calls(Process *p, Export *);
-void trace_gc(Process *p, Eterm what);
+void trace_gc(Process *p, Eterm what, Uint size, Eterm msg);
/* port tracing */
void trace_virtual_sched(Process*, ErtsProcLocks, Eterm);
void trace_sched_ports(Port *pp, Eterm);
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index f3c54de214..f97716d030 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -50,6 +50,7 @@
#define H_DEFAULT_SIZE 233 /* default (heap + stack) min size */
#define VH_DEFAULT_SIZE 32768 /* default virtual (bin) heap min size (words) */
+#define H_DEFAULT_MAX_SIZE 0 /* default max heap size is off */
#define CP_SIZE 1
@@ -160,6 +161,8 @@ extern int num_instructions; /* Number of instruction in opc[]. */
extern int H_MIN_SIZE; /* minimum (heap + stack) */
extern int BIN_VH_MIN_SIZE; /* minimum virtual (bin) heap */
+extern int H_MAX_SIZE; /* maximum (heap + stack) */
+extern int H_MAX_FLAGS; /* maximum heap flags */
extern int erts_atom_table_size;/* Atom table size */
extern int erts_pd_initial_size;/* Initial Process dictionary table size */
diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h
index 43ceeda671..12f68e477b 100644
--- a/erts/emulator/beam/erlang_lttng.h
+++ b/erts/emulator/beam/erlang_lttng.h
@@ -218,13 +218,13 @@ TRACEPOINT_EVENT(
driver_stop,
TP_ARGS(
char*, pid,
- char*, driver,
- char*, port
+ char*, port,
+ char*, driver
),
TP_FIELDS(
ctf_string(pid, pid)
- ctf_string(driver, driver)
ctf_string(port, port)
+ ctf_string(driver, driver)
)
)
@@ -324,7 +324,7 @@ TRACEPOINT_EVENT(
TRACEPOINT_EVENT(
com_ericsson_otp,
- aio_pool_add,
+ aio_pool_put,
TP_ARGS(
char*, port,
int, length
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 723c25ff77..3c002d43a7 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -4429,22 +4429,32 @@ init_done:
SKIP(1+atom_extra_skip);
atom_extra_skip = 0;
break;
- case PID_EXT:
case NEW_PID_EXT:
+ atom_extra_skip = 12;
+ goto case_PID;
+ case PID_EXT:
atom_extra_skip = 9;
+ case_PID:
/* In case it is an external pid */
heap_size += EXTERNAL_THING_HEAD_SIZE + 1;
terms++;
break;
- case PORT_EXT:
case NEW_PORT_EXT:
+ atom_extra_skip = 8;
+ goto case_PORT;
+ case PORT_EXT:
atom_extra_skip = 5;
+ case_PORT:
/* In case it is an external port */
heap_size += EXTERNAL_THING_HEAD_SIZE + 1;
terms++;
break;
- case NEW_REFERENCE_EXT:
case NEWER_REFERENCE_EXT:
+ atom_extra_skip = 4;
+ goto case_NEW_REFERENCE;
+ case NEW_REFERENCE_EXT:
+ atom_extra_skip = 1;
+ case_NEW_REFERENCE:
{
int id_words;
@@ -4455,7 +4465,7 @@ init_done:
goto error;
ep += 2;
- atom_extra_skip = 1 + 4*id_words;
+ atom_extra_skip += 4*id_words;
/* In case it is an external ref */
#if defined(ARCH_64)
heap_size += EXTERNAL_THING_HEAD_SIZE + id_words/2 + 1;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index a7bc990deb..550db95ba8 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -997,7 +997,7 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2);
#define ERTS_CPC_ALLOW_GC (1 << 0)
#define ERTS_CPC_COPY_LITERALS (1 << 1)
#define ERTS_CPC_ALL (ERTS_CPC_ALLOW_GC | ERTS_CPC_COPY_LITERALS)
-Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp);
+Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls);
typedef struct {
Eterm *ptr;
@@ -1483,9 +1483,19 @@ do { \
#define MatchSetGetSource(MPSP) erts_match_set_get_source(MPSP)
-extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr);
+extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA);
Eterm erts_match_set_lint(Process *p, Eterm matchexpr);
extern void erts_match_set_release_result(Process* p);
+ERTS_GLB_INLINE void erts_match_set_release_result_trace(Process* p, Eterm);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE
+void erts_match_set_release_result_trace(Process* p, Eterm pam_result)
+{
+ if (is_not_immed(pam_result))
+ erts_match_set_release_result(p);
+}
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
enum erts_pam_run_flags {
ERTS_PAM_TMP_RESULT=1,
@@ -1493,10 +1503,12 @@ enum erts_pam_run_flags {
ERTS_PAM_CONTIGUOUS_TUPLE=4,
ERTS_PAM_IGNORE_TRACE_SILENT=8
};
-extern Eterm erts_match_set_run(Process *p, Binary *mpsp,
- Eterm *args, int num_args,
- enum erts_pam_run_flags in_flags,
- Uint32 *return_flags);
+extern Eterm erts_match_set_run_trace(Process *p,
+ Process *self,
+ Binary *mpsp,
+ Eterm *args, int num_args,
+ enum erts_pam_run_flags in_flags,
+ Uint32 *return_flags);
extern Eterm erts_match_set_get_source(Binary *mpsp);
extern void erts_match_prog_foreach_offheap(Binary *b,
void (*)(ErlOffHeap *, void *),
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index b14ca77a04..b9f6ab04c6 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1432,10 +1432,11 @@ finalize_force_imm_drv_call(ErtsTryImmDrvCallState *sp)
static ERTS_INLINE void
queue_port_sched_op_reply(Process *rp,
- ErtsProcLocks *rp_locksp,
+ ErtsProcLocks rp_locks,
ErtsHeapFactory* factory,
Uint32 *ref_num,
- Eterm msg)
+ Eterm msg,
+ Port* prt)
{
Eterm* hp = erts_produce_heap(factory, ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE, 0);
Eterm ref;
@@ -1448,11 +1449,12 @@ queue_port_sched_op_reply(Process *rp,
erts_factory_trim_and_close(factory, &msg, 1);
- erts_queue_message(rp, rp_locksp, factory->message, msg);
+ erts_queue_message(rp, rp_locks, factory->message, msg,
+ prt ? prt->common.id : am_undefined);
}
static void
-port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg)
+port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg, Port* prt)
{
Process *rp = erts_proc_lookup_raw(to);
if (rp) {
@@ -1478,10 +1480,11 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg)
factory.off_heap));
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
ref_num,
- msg_copy);
+ msg_copy,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -1651,7 +1654,7 @@ port_badsig(Port *prt, erts_aint32_t state, int op,
state,
sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
return ERTS_PORT_REDS_BADSIG;
} /* port_badsig */
/* bad_port_signal() will
@@ -1820,7 +1823,7 @@ port_sig_outputv(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt);
cleanup_scheduled_outputv(sigdp->u.outputv.evp,
sigdp->u.outputv.cbinp);
@@ -1928,7 +1931,7 @@ port_sig_output(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt);
cleanup_scheduled_output(sigdp->u.output.bufp);
@@ -2636,7 +2639,7 @@ port_sig_exit(Port *prt,
if (sigdp->u.exit.bp)
free_message_buffer(sigdp->u.exit.bp);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt);
return ERTS_PORT_REDS_EXIT;
}
@@ -2829,7 +2832,7 @@ port_sig_connect(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s
msg = am_true;
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt);
return ERTS_PORT_REDS_CONNECT;
}
@@ -2912,7 +2915,7 @@ port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si
if (op == ERTS_PROC2PORT_SIG_EXEC)
port_unlink(prt, sigdp->u.unlink.from);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_UNLINK;
}
@@ -3007,7 +3010,7 @@ port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigd
port_link_failure(sigdp->u.link.port, sigdp->u.link.to);
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_LINK;
}
@@ -3064,7 +3067,8 @@ init_ack_send_reply(Port *port, Eterm resp)
}
port_sched_op_reply(port->async_open_port->to,
port->async_open_port->ref,
- resp);
+ resp,
+ port);
erts_free(ERTS_ALC_T_PRTSD, port->async_open_port);
port->async_open_port = NULL;
@@ -3461,7 +3465,7 @@ deliver_result(Port *prt, Eterm sender, Eterm pid, Eterm res)
sz_res + 3, &hp, &ohp);
res = copy_struct(res, sz_res, &hp, ohp);
tuple = TUPLE2(hp, sender, res);
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, sender);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -3562,7 +3566,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
trace_port_send(prt, to, tuple, 1);
ERL_MESSAGE_TOKEN(mp) = am_undefined;
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
@@ -3734,7 +3738,7 @@ deliver_vec_message(Port* prt, /* Port */
trace_port_send(prt, to, tuple, 1);
ERL_MESSAGE_TOKEN(mp) = am_undefined;
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
erts_proc_dec_refc(rp);
@@ -3861,7 +3865,7 @@ terminate_port(Port *prt)
lttng_decl_procbuf(proc_str);
lttng_pid_to_str(connected_id, proc_str);
lttng_port_to_str(prt, port_str);
- LTTNG3(driver_stop, proc_str, drv->name, port_str);
+ LTTNG3(driver_stop, proc_str, port_str, drv->name);
}
#endif
@@ -4388,10 +4392,11 @@ port_sig_control(Port *prt,
&factory.hp,
factory.off_heap);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- msg);
+ msg,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -4402,7 +4407,7 @@ port_sig_control(Port *prt,
/* failure */
if (sigdp->caller != ERTS_INVALID_PID)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
done:
@@ -4739,10 +4744,11 @@ port_sig_call(Port *prt,
msg = TUPLE2(hp, am_ok, msg);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- msg);
+ msg,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -4754,7 +4760,7 @@ port_sig_call(Port *prt,
}
}
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
done:
@@ -4969,7 +4975,7 @@ port_sig_info(Port *prt,
{
ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
if (op != ERTS_PROC2PORT_SIG_EXEC)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined, prt);
else {
Eterm *hp, *hp_start;
Uint hsz;
@@ -4995,10 +5001,11 @@ port_sig_info(Port *prt,
mp->data.heap_frag = bp;
erts_factory_selfcontained_message_init(&factory, mp, hp);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- value);
+ value,
+ prt);
}
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -5115,7 +5122,7 @@ reply_io_bytes(void *vreq)
msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout);
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (req->sched_id == sched_id)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -5613,7 +5620,7 @@ void driver_report_exit(ErlDrvPort ix, int status)
trace_port_send(prt, pid, tuple, 1);
ERL_MESSAGE_TOKEN(mp) = am_undefined;
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
@@ -6217,15 +6224,20 @@ driver_deliver_term(Port *prt, Eterm to, ErlDrvTermData* data, int len)
done:
if (res > 0) {
+ Eterm from = am_undefined;
mess = ESTACK_POP(stack); /* get resulting value */
erts_factory_trim_and_close(&factory, &mess, 1);
- if (prt && IS_TRACED_FL(prt, F_TRACE_SEND))
- trace_port_send(prt, to, mess, 1);
+ if (prt) {
+ if (IS_TRACED_FL(prt, F_TRACE_SEND)) {
+ trace_port_send(prt, to, mess, 1);
+ }
+ from = prt->common.id;
+ }
/* send message */
ERL_MESSAGE_TOKEN(factory.message) = am_undefined;
- erts_queue_message(rp, &rp_locks, factory.message, mess);
+ erts_queue_message(rp, rp_locks, factory.message, mess, from);
}
else if (res == -2) {
/* this clause only happens when we were requested to
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 748fba15c7..dda3ba565c 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -92,6 +92,9 @@
#define ErtsInArea(ptr,start,nbytes) \
((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
+#define ErtsContainerStruct(ptr, type, member) \
+ (type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))
+
#if defined (__WIN32__)
# include "erl_win_sys.h"
#else
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index b280995488..cedc88e5fe 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -71,41 +71,6 @@
#define HAVE_MALLOPT 0
#endif
-/* profile_scheduler mini message queue */
-
-typedef struct {
- Uint scheduler_id;
- Uint no_schedulers;
- Uint Ms;
- Uint s;
- Uint us;
- Eterm state;
-} profile_sched_msg;
-
-typedef struct {
- profile_sched_msg msg[2];
- Uint n;
-} profile_sched_msg_q;
-
-#ifdef ERTS_SMP
-
-#if 0 /* Unused */
-static void
-dispatch_profile_msg_q(profile_sched_msg_q *psmq)
-{
- int i = 0;
- profile_sched_msg *msg = NULL;
- ASSERT(psmq != NULL);
- for (i = 0; i < psmq->n; i++) {
- msg = &(psmq->msg[i]);
- profile_scheduler_q(make_small(msg->scheduler_id), msg->state, am_undefined, msg->Ms, msg->s, msg->us);
- }
-}
-#endif
-
-#endif
-
-
Eterm*
erts_heap_alloc(Process* p, Uint need, Uint xtra)
{
@@ -2298,7 +2263,7 @@ static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment *
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(p, NULL /* only used for smp build */, mp, message);
+ erts_queue_message(p, 0, mp, message, am_system);
}
#endif
}
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 48cfafb8c5..4063cbf306 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -533,13 +533,13 @@ BIF_RETTYPE hipe_bifs_merge_term_1(BIF_ALIST_1)
BIF_RET(val);
}
-struct mfa {
+struct mfa_t {
Eterm mod;
Eterm fun;
Uint ari;
};
-static int term_to_mfa(Eterm term, struct mfa *mfa)
+static int term_to_mfa(Eterm term, struct mfa_t *mfa)
{
Eterm mod, fun, a;
Uint ari;
@@ -597,7 +597,7 @@ static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity)
Uint *hipe_bifs_find_pc_from_mfa(Eterm term)
{
- struct mfa mfa;
+ struct mfa_t mfa;
if (!term_to_mfa(term, &mfa))
return NULL;
@@ -617,7 +617,7 @@ BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3)
Eterm *pc;
void *address;
int is_closure;
- struct mfa mfa;
+ struct mfa_t mfa;
switch (BIF_ARG_3) {
case am_false:
@@ -1225,7 +1225,7 @@ void hipe_mfa_set_trampoline(Eterm m, Eterm f, unsigned int arity, void *trampol
BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3)
{
- struct mfa mfa;
+ struct mfa_t mfa;
void *address;
int is_exported;
@@ -1247,7 +1247,7 @@ BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3)
BIF_RETTYPE hipe_bifs_invalidate_funinfo_native_addresses_1(BIF_ALIST_1)
{
Eterm lst;
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *p;
hipe_mfa_info_table_rwlock();
@@ -1416,7 +1416,7 @@ BIF_RETTYPE hipe_find_na_or_make_stub(BIF_ALIST_3)
BIF_RETTYPE hipe_bifs_find_na_or_make_stub_2(BIF_ALIST_2)
{
- struct mfa mfa;
+ struct mfa_t mfa;
void *address;
int is_remote;
@@ -1518,9 +1518,9 @@ struct ref {
*/
BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
{
- struct mfa callee;
+ struct mfa_t callee;
Eterm *tuple;
- struct mfa caller;
+ struct mfa_t caller;
void *address;
void *trampoline;
unsigned int flags;
@@ -1597,7 +1597,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
*/
BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */
{
- struct mfa mfa;
+ struct mfa_t mfa;
const struct hipe_mfa_info *p;
struct ref *ref;
@@ -1649,7 +1649,7 @@ static void hipe_purge_all_refs(void)
BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
{
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *caller_mfa, *callee_mfa;
struct hipe_mfa_info_list *refers_to, *tmp_refers_to;
struct ref **prev, *ref;
@@ -1703,7 +1703,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
*/
BIF_RETTYPE hipe_bifs_redirect_referred_from_1(BIF_ALIST_1)
{
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *p;
struct ref **prev, *ref;
int is_remote, res;
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 43144e75ec..f532d3151f 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -59,6 +59,7 @@
* TODO: check PCB consistency at native BIF calls
*/
int hipe_modeswitch_debug = 0;
+extern BeamInstr beam_exit[];
#define HIPE_DEBUG 0
@@ -218,15 +219,37 @@ static __inline__ void hipe_pop_beam_trap_frame(Process *p)
Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
{
unsigned result;
-#if NR_ARG_REGS > 5
- /* When NR_ARG_REGS > 5, we need to protect the process' input
- reduction count (which BEAM stores in def_arg_reg[5]) from
- being clobbered by the arch glue code. */
Eterm reds_in = p->def_arg_reg[5];
-#endif
-#if NR_ARG_REGS > 4
- Eterm o_reds = p->def_arg_reg[4];
-#endif
+ /*
+ * Process is in the normal case scheduled out when reduction
+ * count reach zero. When "save calls" is enabled reduction
+ * count is subtracted with CONTEXT_REDS, i.e. initial reduction
+ * count will be zero or less and process is scheduled out
+ * when -CONTEXT_REDS is reached.
+ *
+ * HiPE does not support the "save calls" feature, so we switch
+ * to using a positive reduction counter when executing in
+ * hipe mode, but need to restore the "save calls" when
+ * returning to beam. We also need to hide the save calls buffer
+ * from BIFs. We do that by moving the saved calls buf to
+ * suspended saved calls buf.
+ *
+ * Beam has initial reduction count in stored in p->def_arg_reg[5].
+ *
+ * Beam expects -neg_o_reds to be found in p->def_arg_reg[4]
+ * on return to beam.
+ */
+
+ {
+ struct saved_calls *scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
+ if (scb) {
+ reds_in += CONTEXT_REDS;
+ p->fcalls += CONTEXT_REDS;
+ ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb);
+ }
+ }
+
+ p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */
p->i = NULL;
/* Set current_function to undefined. stdlib hibernate tests rely on it. */
@@ -481,12 +504,25 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
do_schedule:
{
-#if !(NR_ARG_REGS > 5)
- int reds_in = p->def_arg_reg[5];
+ struct saved_calls *scb;
+
+ scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL);
+ if (scb)
+ ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb);
+
+ /* The process may have died while it was executing,
+ if so we return out from native code to the interpreter */
+ if (erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING)
+ p->i = beam_exit;
+#ifdef DEBUG
+ ASSERT(p->debug_reds_in == reds_in);
#endif
+ p->flags &= ~F_HIPE_MODE;
+
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(p);
p = schedule(p, reds_in - p->fcalls);
ERTS_SMP_REQ_PROC_MAIN_LOCK(p);
+ ASSERT(!(p->flags & F_HIPE_MODE));
#ifdef ERTS_SMP
p->hipe_smp.have_receive_locks = 0;
reg = p->scheduler_data->x_reg_array;
@@ -501,28 +537,32 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
reg[i] = argp[i];
}
{
-#if !(NR_ARG_REGS > 5)
- Eterm reds_in;
-#endif
-#if !(NR_ARG_REGS > 4)
- Eterm o_reds;
-#endif
+ struct saved_calls *scb;
reds_in = p->fcalls;
- o_reds = 0;
- if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
- o_reds = reds_in;
- reds_in = 0;
- p->fcalls = 0;
- }
- p->def_arg_reg[4] = o_reds;
p->def_arg_reg[5] = reds_in;
+#ifdef DEBUG
+ p->debug_reds_in = reds_in;
+#endif
if (p->i == hipe_beam_pc_resume) {
+ p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */
+ scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
+ if (scb)
+ ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb);
p->i = NULL;
p->arity = 0;
goto do_resume;
}
+
+ scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
+ if (!scb)
+ p->def_arg_reg[4] = 0;
+ else {
+ p->def_arg_reg[4] = CONTEXT_REDS;
+ p->fcalls = -CONTEXT_REDS + reds_in;
+ }
}
+
HIPE_CHECK_PCB(p);
result = HIPE_MODE_SWITCH_RES_CALL_BEAM;
p->def_arg_reg[3] = result;
@@ -562,14 +602,29 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
default:
erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: result %#x\r\n", result);
}
+
+ {
+ struct saved_calls *scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL);
+ if (!scb)
+ p->def_arg_reg[4] = 0;
+ else {
+ p->def_arg_reg[4] = CONTEXT_REDS;
+ p->fcalls -= CONTEXT_REDS;
+ ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb);
+ }
+ }
+
HIPE_CHECK_PCB(p);
p->def_arg_reg[3] = result;
-#if NR_ARG_REGS > 4
- p->def_arg_reg[4] = o_reds;
-#endif
#if NR_ARG_REGS > 5
+ /*
+ * When NR_ARG_REGS > 5, we need to protect the process' input
+ * reduction count (which BEAM stores in def_arg_reg[5]) from
+ * being clobbered by the arch glue code.
+ */
p->def_arg_reg[5] = reds_in;
#endif
+ p->flags &= ~F_HIPE_MODE;
return p;
}
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 00b572029a..9c03b3811c 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -601,7 +601,7 @@ void hipe_clear_timeout(Process *c_p)
}
#endif
if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) {
- trace_receive(c_p, am_timeout);
+ trace_receive(c_p, am_clock_service, am_timeout, NULL);
}
c_p->flags &= ~F_TIMO;
JOIN_MESSAGE(c_p);
diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c
index a1e0e581a4..8a9a1bf16c 100644
--- a/erts/emulator/nifs/common/erl_tracer_nif.c
+++ b/erts/emulator/nifs/common/erl_tracer_nif.c
@@ -72,6 +72,10 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload)
ATOM_DECL(trace); \
ATOM_DECL(trace_ts); \
ATOM_DECL(true); \
+ ATOM_DECL(gc_minor_start); \
+ ATOM_DECL(gc_minor_end); \
+ ATOM_DECL(gc_major_start); \
+ ATOM_DECL(gc_major_end); \
ATOM_DECL(undefined);
#define ATOM_DECL(A) static ERL_NIF_TERM atom_##A
@@ -124,6 +128,9 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
if (!enif_is_port_alive(env, &to_port))
/* tracer is dead so we should remove this trace point */
return atom_remove;
+ } else {
+ /* The state was not a pid or a port */
+ return atom_remove;
}
/* Only generate trace for when tracer != tracee */
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index a7e710070b..53009a1481 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -28,6 +28,8 @@
#include "erl_mmap.h"
#include <stddef.h>
+#if HAVE_ERTS_MMAP
+
/* #define ERTS_MMAP_OP_RINGBUF_SZ 100 */
#if defined(DEBUG) || 0
@@ -2099,6 +2101,7 @@ int erts_mmap_in_supercarrier(ErtsMemMapper* mm, void *ptr)
}
static struct {
+ Eterm options;
Eterm total;
Eterm total_sa;
Eterm total_sua;
@@ -2132,6 +2135,7 @@ static void init_atoms(void)
erts_mtx_lock(&am.init_mutex);
if (!am.is_initialized) {
+ AM_INIT(options);
AM_INIT(total);
AM_INIT(total_sa);
AM_INIT(total_sua);
@@ -2387,9 +2391,9 @@ Eterm erts_mmap_info(ErtsMemMapper* mm,
Eterm seg_tags[] = { am.used, am.max, am.allocated, am.reserved, am.used_sa, am.used_sua };
Eterm group[2];
Eterm group_tags[] = { am.sizes, am.free_segs };
- Eterm list[2];
- Eterm list_tags[2]; /* { am.supercarrier, am.os } */
- int lix;
+ Eterm list[3];
+ Eterm list_tags[3]; /* { am.options, am.supercarrier, am.os } */
+ int lix = 0;
Eterm res = THE_NON_VALUE;
if (!hpp) {
@@ -2412,6 +2416,12 @@ Eterm erts_mmap_info(ErtsMemMapper* mm,
erts_smp_mtx_unlock(&mm->mtx);
}
+ list[lix] = erts_mmap_info_options(mm, "option ", print_to_p, print_to_arg,
+ hpp, szp);
+ list_tags[lix] = am.options;
+ lix++;
+
+
if (print_to_p) {
int to = *print_to_p;
void *arg = print_to_arg;
@@ -2441,7 +2451,6 @@ Eterm erts_mmap_info(ErtsMemMapper* mm,
init_atoms();
}
- lix = 0;
if (mm->supercarrier) {
group[0] = erts_bld_atom_uword_2tup_list(hpp, szp,
sizeof(size_tags)/sizeof(Eterm),
@@ -2506,9 +2515,13 @@ Eterm erts_mmap_info_options(ErtsMemMapper* mm,
return res;
}
+#endif /* HAVE_ERTS_MMAP */
-Eterm erts_mmap_debug_info(ErtsMemMapper* mm, Process* p)
+Eterm erts_mmap_debug_info(Process* p)
{
+#if HAVE_ERTS_MMAP
+ ErtsMemMapper* mm = &erts_dflt_mmapper;
+
if (mm->supercarrier) {
ERTS_DECL_AM(sabot);
ERTS_DECL_AM(satop);
@@ -2546,9 +2559,8 @@ Eterm erts_mmap_debug_info(ErtsMemMapper* mm, Process* p)
HRelease(p, hp_end, hp);
return list;
}
- else {
- return am_undefined;
- }
+#endif
+ return am_undefined;
}
diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h
index b3c45ba116..7ac61a82c1 100644
--- a/erts/emulator/sys/common/erl_mmap.h
+++ b/erts/emulator/sys/common/erl_mmap.h
@@ -26,9 +26,36 @@
#define ERTS_MMAP_SUPERALIGNED_BITS (18)
/* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */
-#define ERTS_MMAPFLG_OS_ONLY (((Uint32) 1) << 0)
-#define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1)
-#define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2)
+#ifndef HAVE_MMAP
+# define HAVE_MMAP 0
+#endif
+#ifndef HAVE_MREMAP
+# define HAVE_MREMAP 0
+#endif
+#if HAVE_MMAP
+# define ERTS_HAVE_OS_MMAP 1
+# define ERTS_HAVE_GENUINE_OS_MMAP 1
+# if HAVE_MREMAP
+# define ERTS_HAVE_OS_MREMAP 1
+# endif
+# if defined(MAP_FIXED) && defined(MAP_NORESERVE)
+# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1
+# endif
+#endif
+
+#ifndef HAVE_VIRTUALALLOC
+# define HAVE_VIRTUALALLOC 0
+#endif
+#if HAVE_VIRTUALALLOC
+# define ERTS_HAVE_OS_MMAP 1
+#endif
+
+#ifdef ERTS_HAVE_GENUINE_OS_MMAP
+# define HAVE_ERTS_MMAP 1
+#else
+# define HAVE_ERTS_MMAP 0
+#endif
+
extern UWord erts_page_inv_mask;
@@ -60,26 +87,6 @@ typedef struct {
#define ERTS_MMAP_INIT_HIPE_EXEC_INITER \
{{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
-typedef struct ErtsMemMapper_ ErtsMemMapper;
-
-void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep);
-void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size);
-void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep);
-int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr);
-void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable);
-struct erts_mmap_info_struct
-{
- UWord sizes[6];
- UWord segs[6];
- UWord os_used;
-};
-Eterm erts_mmap_info(ErtsMemMapper*, int *print_to_p, void *print_to_arg,
- Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*);
-Eterm erts_mmap_info_options(ErtsMemMapper*,
- char *prefix, int *print_to_p, void *print_to_arg,
- Uint **hpp, Uint *szp);
-struct process;
-Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*);
#define ERTS_SUPERALIGNED_SIZE \
(1 << ERTS_MMAP_SUPERALIGNED_BITS)
@@ -107,29 +114,34 @@ Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*);
#define ERTS_PAGEALIGNED_SIZE \
(ERTS_INV_PAGEALIGNED_MASK + 1)
-#ifndef HAVE_MMAP
-# define HAVE_MMAP 0
-#endif
-#ifndef HAVE_MREMAP
-# define HAVE_MREMAP 0
-#endif
-#if HAVE_MMAP
-# define ERTS_HAVE_OS_MMAP 1
-# define ERTS_HAVE_GENUINE_OS_MMAP 1
-# if HAVE_MREMAP
-# define ERTS_HAVE_OS_MREMAP 1
-# endif
-# if defined(MAP_FIXED) && defined(MAP_NORESERVE)
-# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1
-# endif
-#endif
+struct process;
+Eterm erts_mmap_debug_info(struct process*);
+
+#if HAVE_ERTS_MMAP
+
+typedef struct ErtsMemMapper_ ErtsMemMapper;
+
+#define ERTS_MMAPFLG_OS_ONLY (((Uint32) 1) << 0)
+#define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1)
+#define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2)
+
+void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep);
+void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size);
+void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep);
+int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr);
+void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable);
+struct erts_mmap_info_struct
+{
+ UWord sizes[6];
+ UWord segs[6];
+ UWord os_used;
+};
+Eterm erts_mmap_info(ErtsMemMapper*, int *print_to_p, void *print_to_arg,
+ Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*);
+Eterm erts_mmap_info_options(ErtsMemMapper*,
+ char *prefix, int *print_to_p, void *print_to_arg,
+ Uint **hpp, Uint *szp);
-#ifndef HAVE_VIRTUALALLOC
-# define HAVE_VIRTUALALLOC 0
-#endif
-#if HAVE_VIRTUALALLOC
-# define ERTS_HAVE_OS_MMAP 1
-#endif
#ifdef ERTS_WANT_MEM_MAPPERS
# include "erl_alloc_types.h"
@@ -154,4 +166,6 @@ void hard_dbg_remove_mseg(void* seg, UWord sz);
# define HARD_DBG_REMOVE_MSEG(SEG,SZ)
#endif
+#endif /* HAVE_ERTS_MMAP */
+
#endif /* ERL_MMAP_H__ */
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index 43e75e2573..f3306a888c 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -996,10 +996,7 @@ info_options(ErtsMsegAllctr_t *ma,
Uint **hpp,
Uint *szp)
{
- Eterm res;
-
- res = erts_mmap_info_options(&erts_dflt_mmapper,
- prefix, print_to_p, print_to_arg, hpp, szp);
+ Eterm res = NIL;
if (print_to_p) {
int to = *print_to_p;
@@ -1110,7 +1107,7 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp
static Eterm
info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg,
- int begin_new_max_period, Uint **hpp, Uint *szp)
+ int begin_new_max_period, int only_sz, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -1123,38 +1120,41 @@ info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg,
int to = *print_to_p;
void *arg = print_to_arg;
- erts_print(to, arg, "cached_segments: %beu\n", ma->cache_size);
- erts_print(to, arg, "cache_hits: %beu\n", ma->cache_hits);
- erts_print(to, arg, "segments: %beu %beu %beu\n",
- ma->segments.current.no, ma->segments.max.no, ma->segments.max_ever.no);
- erts_print(to, arg, "segments_size: %beu %beu %beu\n",
+ if (!only_sz) {
+ erts_print(to, arg, "cached_segments: %beu\n", ma->cache_size);
+ erts_print(to, arg, "cache_hits: %beu\n", ma->cache_hits);
+ erts_print(to, arg, "segments: %beu %beu %beu\n",
+ ma->segments.current.no, ma->segments.max.no, ma->segments.max_ever.no);
+ erts_print(to, arg, "segments_watermark: %beu\n",
+ ma->segments.current.watermark);
+ }
+ erts_print(to, arg, "segments_size: %beu %beu %beu\n",
ma->segments.current.sz, ma->segments.max.sz, ma->segments.max_ever.sz);
- erts_print(to, arg, "segments_watermark: %beu\n",
- ma->segments.current.watermark);
}
if (hpp || szp) {
res = NIL;
- add_2tup(hpp, szp, &res,
- am.segments_watermark,
- bld_unstable_uint(hpp, szp, ma->segments.current.watermark));
- add_4tup(hpp, szp, &res,
- am.segments_size,
- bld_unstable_uint(hpp, szp, ma->segments.current.sz),
- bld_unstable_uint(hpp, szp, ma->segments.max.sz),
- bld_unstable_uint(hpp, szp, ma->segments.max_ever.sz));
- add_4tup(hpp, szp, &res,
- am.segments,
- bld_unstable_uint(hpp, szp, ma->segments.current.no),
- bld_unstable_uint(hpp, szp, ma->segments.max.no),
- bld_unstable_uint(hpp, szp, ma->segments.max_ever.no));
- add_2tup(hpp, szp, &res,
- am.cache_hits,
- bld_unstable_uint(hpp, szp, ma->cache_hits));
- add_2tup(hpp, szp, &res,
- am.cached_segments,
- bld_unstable_uint(hpp, szp, ma->cache_size));
-
+ add_4tup(hpp, szp, &res,
+ am.segments_size,
+ bld_unstable_uint(hpp, szp, ma->segments.current.sz),
+ bld_unstable_uint(hpp, szp, ma->segments.max.sz),
+ bld_unstable_uint(hpp, szp, ma->segments.max_ever.sz));
+ if (!only_sz) {
+ add_2tup(hpp, szp, &res,
+ am.segments_watermark,
+ bld_unstable_uint(hpp, szp, ma->segments.current.watermark));
+ add_4tup(hpp, szp, &res,
+ am.segments,
+ bld_unstable_uint(hpp, szp, ma->segments.current.no),
+ bld_unstable_uint(hpp, szp, ma->segments.max.no),
+ bld_unstable_uint(hpp, szp, ma->segments.max_ever.no));
+ add_2tup(hpp, szp, &res,
+ am.cache_hits,
+ bld_unstable_uint(hpp, szp, ma->cache_hits));
+ add_2tup(hpp, szp, &res,
+ am.cached_segments,
+ bld_unstable_uint(hpp, szp, ma->cache_size));
+ }
}
if (begin_new_max_period) {
@@ -1166,26 +1166,31 @@ info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg,
}
static Eterm info_memkind(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg,
- int begin_max_per, Uint **hpp, Uint *szp)
+ int begin_max_per, int only_sz, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
Eterm atoms[3];
Eterm values[3];
- if (print_to_p) {
- erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", "all memory");
- }
- if (hpp || szp) {
- atoms[0] = am.name;
- atoms[1] = am.status;
- atoms[2] = am.calls;
- values[0] = erts_bld_string(hpp, szp, "all memory");
+ if (!only_sz) {
+ if (print_to_p) {
+ erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", "all memory");
+ }
+ if (hpp || szp) {
+ atoms[0] = am.name;
+ atoms[1] = am.status;
+ atoms[2] = am.calls;
+ values[0] = erts_bld_string(hpp, szp, "all memory");
+ }
}
- values[1] = info_status(ma, print_to_p, print_to_arg, begin_max_per, hpp, szp);
- values[2] = info_calls(ma, print_to_p, print_to_arg, hpp, szp);
+ res = info_status(ma, print_to_p, print_to_arg, begin_max_per, only_sz, hpp, szp);
+ if (!only_sz) {
+ values[1] = res;
+ values[2] = info_calls(ma, print_to_p, print_to_arg, hpp, szp);
- if (hpp || szp)
- res = bld_2tup_list(hpp, szp, 3, atoms, values);
+ if (hpp || szp)
+ res = bld_2tup_list(hpp, szp, 3, atoms, values);
+ }
return res;
}
@@ -1229,6 +1234,7 @@ erts_mseg_info(int ix,
int *print_to_p,
void *print_to_arg,
int begin_max_per,
+ int only_sz,
Uint **hpp,
Uint *szp)
{
@@ -1239,24 +1245,29 @@ erts_mseg_info(int ix,
Uint n = 0;
if (hpp || szp) {
-
- if (!atoms_initialized)
- init_atoms(ma);
-
- atoms[0] = am.version;
- atoms[1] = am.options;
- atoms[2] = am.memkind;
- atoms[3] = am.memkind;
+ if (!atoms_initialized)
+ init_atoms(ma);
+ }
+ if (!only_sz) {
+ if (hpp || szp) {
+ atoms[0] = am.version;
+ atoms[1] = am.options;
+ atoms[2] = am.memkind;
+ }
+ values[n++] = info_version(ma, print_to_p, print_to_arg, hpp, szp);
+ values[n++] = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp);
}
- values[n++] = info_version(ma, print_to_p, print_to_arg, hpp, szp);
- values[n++] = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp);
ERTS_MSEG_LOCK(ma);
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- values[n++] = info_memkind(ma, print_to_p, print_to_arg, begin_max_per, hpp, szp);
- if (hpp || szp)
- res = bld_2tup_list(hpp, szp, n, atoms, values);
+ res = info_memkind(ma, print_to_p, print_to_arg, begin_max_per, only_sz, hpp, szp);
+
+ if (!only_sz) {
+ values[n++] = res;
+ if (hpp || szp)
+ res = bld_2tup_list(hpp, szp, n, atoms, values);
+ }
ERTS_MSEG_UNLOCK(ma);
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index 192e5767e4..a43b409e94 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -99,7 +99,7 @@ void erts_mseg_init(ErtsMsegInit_t *init);
void erts_mseg_late_init(void); /* Have to be called after all allocators,
threads and timers have been initialized. */
Eterm erts_mseg_info_options(int, int *, void*, Uint **, Uint *);
-Eterm erts_mseg_info(int, int *, void*, int, Uint **, Uint *);
+Eterm erts_mseg_info(int, int *, void*, int, int, Uint **, Uint *);
#endif /* #if HAVE_ERTS_MSEG */
diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c
index 60661d9016..6435da086f 100644
--- a/erts/emulator/sys/unix/sys_float.c
+++ b/erts/emulator/sys/unix/sys_float.c
@@ -53,7 +53,7 @@ static void erts_init_fp_exception(void)
void erts_thread_init_fp_exception(void)
{
unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe));
- *fpe = 0L;
+ *fpe = 0;
erts_tsd_set(fpe_key, fpe);
}
@@ -102,6 +102,17 @@ void erts_fp_check_init_error(volatile unsigned long *fpexnp)
#define __DARWIN__ 1
#endif
+/*
+ * Define two processor and possibly OS-specific primitives:
+ *
+ * static void unmask_fpe(void);
+ * -- unmask invalid, overflow, and divide-by-zero exceptions
+ *
+ * static int mask_fpe(void);
+ * -- mask invalid, overflow, and divide-by-zero exceptions
+ * -- return non-zero if the previous state was unmasked
+ */
+
#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
static void unmask_x87(void)
@@ -113,7 +124,6 @@ static void unmask_x87(void)
__asm__ __volatile__("fldcw %0" : : "m"(cw));
}
-/* mask x87 FPE, return true if the previous state was unmasked */
static int mask_x87(void)
{
unsigned short cw;
@@ -136,7 +146,6 @@ static void unmask_sse2(void)
__asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
}
-/* mask SSE2 FPE, return true if the previous state was unmasked */
static int mask_sse2(void)
{
unsigned int mxcsr;
@@ -257,21 +266,19 @@ static int cpu_has_sse2(void)
}
#endif /* !__x86_64__ */
-static void unmask_fpe(void)
+static void unmask_fpe_internal(void)
{
- __asm__ __volatile__("fnclex");
unmask_x87();
if (cpu_has_sse2())
unmask_sse2();
}
-static void unmask_fpe_conditional(int unmasked)
+static void unmask_fpe(void)
{
- if (unmasked)
- unmask_fpe();
+ __asm__ __volatile__("fnclex");
+ unmask_fpe_internal();
}
-/* mask x86 FPE, return true if the previous state was unmasked */
static int mask_fpe(void)
{
int unmasked;
@@ -285,9 +292,7 @@ static int mask_fpe(void)
void erts_restore_fpu(void)
{
__asm__ __volatile__("fninit");
- unmask_x87();
- if (cpu_has_sse2())
- unmask_sse2();
+ unmask_fpe_internal();
}
#elif defined(__sparc__) && defined(__linux__)
@@ -310,13 +315,6 @@ static void unmask_fpe(void)
__asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
}
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask SPARC FPE, return true if the previous state was unmasked */
static int mask_fpe(void)
{
unsigned long fsr;
@@ -431,13 +429,6 @@ static void unmask_fpe(void)
set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */
}
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask PowerPC FPE, return true if the previous state was unmasked */
static int mask_fpe(void)
{
int unmasked;
@@ -447,20 +438,13 @@ static int mask_fpe(void)
return unmasked;
}
-#else
+#else /* !(x86 || (sparc && linux) || (powerpc && (linux || darwin))) */
static void unmask_fpe(void)
{
fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ);
}
-static void unmask_fpe_conditional(int unmasked)
-{
- if (unmasked)
- unmask_fpe();
-}
-
-/* mask IEEE FPE, return true if previous state was unmasked */
static int mask_fpe(void)
{
const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ;
@@ -472,6 +456,16 @@ static int mask_fpe(void)
#endif
+/*
+ * Define a processor and OS-specific SIGFPE handler.
+ *
+ * The effect of receiving a SIGFPE should be:
+ * 1. Update the processor context:
+ * a) on x86: mask FP exceptions, do not skip faulting instruction
+ * b) on SPARC and PowerPC: unmask FP exceptions, skip faulting instruction
+ * 2. call set_current_fp_exception with the PC of the faulting instruction
+ */
+
#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))
#if defined(__linux__) && defined(__i386__)
@@ -520,8 +514,7 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
ucontext_t *uc = puc;
unsigned long pc;
-#if defined(__linux__)
-#if defined(__x86_64__)
+#if defined(__linux__) && defined(__x86_64__)
mcontext_t *mc = &uc->uc_mcontext;
fpregset_t fpstate = mc->fpregs;
pc = mc_pc(mc);
@@ -533,26 +526,26 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
set encoding makes that a poor solution here. */
fpstate->mxcsr = 0x1F80;
fpstate->swd &= ~0xFF;
-#elif defined(__i386__)
+#elif defined(__linux__) && defined(__i386__)
mcontext_t *mc = &uc->uc_mcontext;
fpregset_t fpstate = mc->fpregs;
pc = mc_pc(mc);
if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
fpstate->sw &= ~0xFF;
-#elif defined(__sparc__) && defined(__arch64__)
+#elif defined(__linux__) && defined(__sparc__) && defined(__arch64__)
/* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
struct sigcontext *sc = (struct sigcontext*)puc;
pc = sc->sigc_regs.tpc;
sc->sigc_regs.tpc = sc->sigc_regs.tnpc;
sc->sigc_regs.tnpc += 4;
-#elif defined(__sparc__)
+#elif defined(__linux__) && defined(__sparc__)
/* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
struct sigcontext *sc = (struct sigcontext*)puc;
pc = sc->si_regs.pc;
sc->si_regs.pc = sc->si_regs.npc;
sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4;
-#elif defined(__powerpc__)
+#elif defined(__linux__) && defined(__powerpc__)
#if defined(__powerpc64__)
mcontext_t *mc = &uc->uc_mcontext;
unsigned long *regs = &mc->gp_regs[0];
@@ -563,7 +556,6 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
pc = regs[PT_NIP];
regs[PT_NIP] += 4;
regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */
-#endif
#elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__))
# error "Floating-point exceptions not supported on MacOS X"
#elif defined(__DARWIN__) && defined(__ppc__)
@@ -621,7 +613,7 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
#endif
#if 0
{
- char buf[64];
+ char buf[128];
snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc);
write(2, buf, strlen(buf));
}
@@ -706,7 +698,8 @@ int erts_sys_block_fpe(void)
void erts_sys_unblock_fpe(int unmasked)
{
- unmask_fpe_conditional(unmasked);
+ if (unmasked)
+ unmask_fpe();
}
#endif
@@ -818,11 +811,6 @@ sys_chars_to_double(char* buf, double* fp)
int
matherr(struct exception *exc)
{
-#if !defined(NO_FPE_SIGNALS)
- volatile unsigned long *fpexnp = erts_get_current_fp_exception();
- if (fpexnp != NULL)
- *fpexnp = (unsigned long)__builtin_return_address(0);
-#endif
return 1;
}
diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c
index a2b9bd1263..2b2d6ab7d3 100644
--- a/erts/emulator/sys/win32/sys_float.c
+++ b/erts/emulator/sys/win32/sys_float.c
@@ -139,8 +139,7 @@ sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t deci
int
matherr(struct _exception *exc)
{
- erl_fp_exception = 1;
- DEBUGF(("FP exception (matherr) (0x%x) (%d)\n", exc->type, erl_fp_exception));
+ DEBUGF(("FP exception (matherr) (0x%x)\n", exc->type));
return 1;
}
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 5fb560d1ec..1f7b499dcb 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -102,8 +102,8 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
Self = self(),
Ref = make_ref(),
F = fun() ->
- SI = erlang:system_info({allocator,mseg_alloc}),
- {erts_mmap,EM} = lists:keyfind(erts_mmap, 1, SI),
+ SI = erlang:system_info({allocator,erts_mmap}),
+ {default_mmap,EM} = lists:keyfind(default_mmap, 1, SI),
{supercarrier,SC} = lists:keyfind(supercarrier, 1, EM),
{sizes,Sizes} = lists:keyfind(sizes, 1, SC),
{free_segs,Segs} = lists:keyfind(free_segs,1,SC),
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index d0f6292d5b..efc79f42ed 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -89,8 +89,8 @@ end_per_testcase(Case, _Config) ->
%% com_ericsson_otp:carrier_pool_put
%% com_ericsson_otp:carrier_destroy
%% com_ericsson_otp:carrier_create
-%% com_ericsson_otp:aio_pool_add
-%% com_ericsson_otp:aio_pool_get
+%% com_ericsson_otp:aio_pool_put
+%% com_ericsson_otp:aio_pool_get
%% com_ericsson_otp:driver_control
%% com_ericsson_otp:driver_call
%% com_ericsson_otp:driver_finish
@@ -151,7 +151,7 @@ t_memory_carrier(Config) ->
ok
end.
-%% com_ericsson_otp:aio_pool_add
+%% com_ericsson_otp:aio_pool_put
%% com_ericsson_otp:aio_pool_get
t_async_io_pool(Config) ->
case have_async_threads() of
@@ -168,7 +168,7 @@ t_async_io_pool(Config) ->
{ok, _} = file:list_dir(Path2),
Res = lttng_stop_and_view(Config),
- ok = check_tracepoint("com_ericsson_otp:aio_pool_add", Res),
+ ok = check_tracepoint("com_ericsson_otp:aio_pool_put", Res),
ok = check_tracepoint("com_ericsson_otp:aio_pool_get", Res),
ok
end.
@@ -416,7 +416,7 @@ txt() ->
"%% com_ericsson_otp:carrier_pool_put\n"
"%% com_ericsson_otp:carrier_destroy\n"
"%% com_ericsson_otp:carrier_create\n"
- "%% com_ericsson_otp:aio_pool_add\n"
+ "%% com_ericsson_otp:aio_pool_put\n"
"%% com_ericsson_otp:aio_pool_get\n"
"%% com_ericsson_otp:driver_control\n"
"%% com_ericsson_otp:driver_call\n"
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 956b82335c..b3870f0313 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -48,6 +48,7 @@
t_bif_map_new/1,
t_bif_map_put/1,
t_bif_map_remove/1,
+ t_bif_map_take/1, t_bif_map_take_large/1,
t_bif_map_update/1,
t_bif_map_values/1,
t_bif_map_to_list/1,
@@ -112,7 +113,9 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large,
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_remove,
+ t_bif_map_take, t_bif_map_take_large,
+ t_bif_map_update,
t_bif_map_values,
t_bif_map_to_list, t_bif_map_from_list,
@@ -1970,7 +1973,7 @@ 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},
+ 4 => number, 18446744073709551629 => wat},
M1 = maps:remove("hi", M0),
true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)),
@@ -1999,10 +2002,71 @@ t_bif_map_remove(Config) when is_list(Config) ->
%% error case
do_badmap(fun(T) ->
- {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} =
- (catch maps:remove(a, T))
+ {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} = (catch maps:remove(a, T))
end),
- ok.
+ ok.
+
+t_bif_map_take(Config) when is_list(Config) ->
+ error = maps:take(some_key, #{}),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ 5 = maps:size(M0),
+ {"hello", M1} = maps:take("hi", M0),
+ true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)),
+ true = is_members([number,wat,3,<<"value">>],maps:values(M1)),
+ error = maps:take("hi", M1),
+ 4 = maps:size(M1),
+
+ {3, M2} = maps:take(int, M1),
+ true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)),
+ true = is_members([number,wat,<<"value">>],maps:values(M2)),
+ error = maps:take(int, M2),
+ 3 = maps:size(M2),
+
+ {<<"value">>,M3} = maps:take(<<"key">>, M2),
+ true = is_members([4,18446744073709551629],maps:keys(M3)),
+ true = is_members([number,wat],maps:values(M3)),
+ error = maps:take(<<"key">>, M3),
+ 2 = maps:size(M3),
+
+ {wat,M4} = maps:take(18446744073709551629, M3),
+ true = is_members([4],maps:keys(M4)),
+ true = is_members([number],maps:values(M4)),
+ error = maps:take(18446744073709551629, M4),
+ 1 = maps:size(M4),
+
+ {number,M5} = maps:take(4, M4),
+ [] = maps:keys(M5),
+ [] = maps:values(M5),
+ error = maps:take(4, M5),
+ 0 = maps:size(M5),
+
+ {wat,#{ "hi" := "hello", int := 3, 4 := number, <<"key">> := <<"value">>}} = maps:take(18446744073709551629,M0),
+
+ %% error case
+ do_badmap(fun(T) ->
+ {'EXIT',{{badmap,T},[{maps,take,_,_}|_]}} = (catch maps:take(a, T))
+ end),
+ ok.
+
+t_bif_map_take_large(Config) when is_list(Config) ->
+ KVs = [{{erlang:md5(<<I:64>>),I}, I}|| I <- lists:seq(1,500)],
+ M0 = maps:from_list(KVs),
+ ok = bif_map_take_all(KVs, M0),
+ ok.
+
+bif_map_take_all([], M0) ->
+ 0 = maps:size(M0),
+ ok;
+bif_map_take_all([{K,V}|KVs],M0) ->
+ {ok,V} = maps:find(K,M0),
+ {V,M1} = maps:take(K,M0),
+ error = maps:find(K,M1),
+ error = maps:take(K,M1),
+ bif_map_take_all(KVs,M1).
+
t_bif_map_update(Config) when is_list(Config) ->
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index ea973276db..6733237b20 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -597,16 +597,15 @@ ms_trace3(Config) when is_list(Config) ->
end),
ok.
-ms_trace_dead(doc) ->
- ["Test that a dead tracer is removed using ms"];
-ms_trace_dead(suite) -> [];
-ms_trace_dead(Config) when is_list(Config) ->
+%% Test that a dead tracer is removed using ms
+ms_trace_dead(_Config) ->
Self = self(),
TFun = fun F() -> receive M -> Self ! M, F() end end,
{Tracer, MRef} = spawn_monitor(TFun),
MetaTracer = spawn_link(TFun),
erlang:trace_pattern({?MODULE, f1, '_'},
- [{'_',[],[{trace,[],
+ [{'_',[],[{message, false},
+ {trace,[],
[call,{const,{tracer,Tracer}}]}]}],
[{meta, MetaTracer}]),
erlang:trace_pattern({?MODULE, f2, '_'}, []),
@@ -623,8 +622,6 @@ ms_trace_dead(Config) when is_list(Config) ->
?MODULE:f2(3,4),
TRef = erlang:trace_delivered(all),
receive {trace_delivered, _, TRef} -> ok end,
- receive {trace_ts, Self, call, {?MODULE, f1, _}, _} -> ok end,
- receive {trace_ts, Self, call, {?MODULE, f1, _}, _} -> ok end,
receive M -> ct:fail({unexpected, M}) after 10 -> ok end.
%% Test that destructive operations in test bif does not really happen
diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl
index 6efca5b39e..226462676c 100644
--- a/erts/emulator/test/message_queue_data_SUITE.erl
+++ b/erts/emulator/test/message_queue_data_SUITE.erl
@@ -21,7 +21,7 @@
-module(message_queue_data_SUITE).
-export([all/0, suite/0]).
--export([basic/1, process_info_messages/1]).
+-export([basic/1, process_info_messages/1, total_heap_size/1]).
-export([basic_test/1]).
@@ -32,7 +32,7 @@ suite() ->
{timetrap, {minutes, 2}}].
all() ->
- [basic, process_info_messages].
+ [basic, process_info_messages, total_heap_size].
%%
%%
@@ -44,15 +44,15 @@ basic(Config) when is_list(Config) ->
basic_test(erlang:system_info(message_queue_data)),
- {ok, Node1} = start_node(Config, "+xmqd off_heap"),
+ {ok, Node1} = start_node(Config, "+hmqd off_heap"),
ok = rpc:call(Node1, ?MODULE, basic_test, [off_heap]),
stop_node(Node1),
- {ok, Node2} = start_node(Config, "+xmqd on_heap"),
+ {ok, Node2} = start_node(Config, "+hmqd on_heap"),
ok = rpc:call(Node2, ?MODULE, basic_test, [on_heap]),
stop_node(Node2),
- {ok, Node3} = start_node(Config, "+xmqd mixed"),
+ {ok, Node3} = start_node(Config, "+hmqd mixed"),
ok = rpc:call(Node3, ?MODULE, basic_test, [mixed]),
stop_node(Node3),
@@ -190,6 +190,28 @@ process_info_messages(Config) when is_list(Config) ->
ok.
+total_heap_size(_Config) ->
+
+ Fun = fun F() -> receive Pid when is_pid(Pid) -> Pid ! ok,F() end end,
+
+ %% Test that on_heap messages grow the heap even if they are not received
+ OnPid = spawn_opt(Fun, [{message_queue_data, on_heap}]),
+ {total_heap_size, OnSize} = erlang:process_info(OnPid, total_heap_size),
+ [OnPid ! lists:duplicate(N,N) || N <- lists:seq(1,100)],
+ OnPid ! self(), receive ok -> ok end,
+ {total_heap_size, OnSizeAfter} = erlang:process_info(OnPid, total_heap_size),
+ ct:log("OnSize = ~p, OnSizeAfter = ~p",[OnSize, OnSizeAfter]),
+ true = OnSize < OnSizeAfter,
+
+ %% Test that off_heap messages do not grow the heap if they are not received
+ OffPid = spawn_opt(Fun, [{message_queue_data, off_heap}]),
+ {total_heap_size, OffSize} = erlang:process_info(OffPid, total_heap_size),
+ [OffPid ! lists:duplicate(N,N) || N <- lists:seq(1,100)],
+ OffPid ! self(), receive ok -> ok end,
+ {total_heap_size, OffSizeAfter} = erlang:process_info(OffPid, total_heap_size),
+ ct:log("OffSize = ~p, OffSizeAfter = ~p",[OffSize, OffSizeAfter]),
+ true = OffSize == OffSizeAfter.
+
%%
%%
%% helpers
diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl
index 41e8a316c4..bfdea0761b 100644
--- a/erts/emulator/test/port_trace_SUITE.erl
+++ b/erts/emulator/test/port_trace_SUITE.erl
@@ -240,19 +240,23 @@ command(Config) ->
Prt ! {S, {command, <<?ECHO_DRV_NOOP:8>>}},
[{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_NOOP:8>>}}}] = flush(),
+ OutputMsg = <<?ECHO_DRV_NOOP:8,0:(8*512)>>,
+ Prt ! {S, {command, OutputMsg}},
+ [{trace, Prt, 'receive', {S, {command, OutputMsg}}}] = flush(),
+
close(Prt, Flags),
os:putenv("OUTPUTV","true"),
reload_drv(Config),
Prt2 = erlang:open_port({spawn, echo_drv}, [binary]),
- Msg = [<<0:8>>,<<0:(8*512)>>,<<0:(8*256)>>,<<0:8>>],
+ OutputvMsg = [<<0:8>>,<<0:(8*512)>>,<<0:(8*256)>>,<<0:8>>],
- erlang:port_command(Prt2, Msg),
- [{trace, Prt2, 'receive', {S, {command, Msg}}}] = flush(),
+ erlang:port_command(Prt2, OutputvMsg),
+ [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(),
- Prt2 ! {S, {command, Msg}},
- [{trace, Prt2, 'receive', {S, {command, Msg}}}] = flush(),
+ Prt2 ! {S, {command, OutputvMsg}},
+ [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(),
close(Prt2, Flags),
@@ -274,6 +278,12 @@ control(_Config) ->
[{trace, Prt, 'receive', {S, {control, {(1 bsl 32) - 1, <<?ECHO_DRV_NOOP:8, 0:8>>}}}},
{trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(),
+ Msg = <<?ECHO_DRV_NOOP:8, 0:(8*512)>>,
+ Pat = lists:duplicate(512, 0),
+ Pat = erlang:port_control(Prt, 1, Msg),
+ [{trace, Prt, 'receive', {S, {control, {1, Msg}}}},
+ {trace, Prt, send, {Prt, {control, <<0:(8*512)>>}}, S}] = flush(),
+
close(Prt, Flags),
ok.
@@ -331,12 +341,16 @@ call(_Config) ->
Flags = [send,'receive'],
{Prt, S} = trace_and_open(Flags,[binary]),
- Msg = {hello, world, make_ref()},
- BinMsg = term_to_binary(Msg),
+ Test = fun(Msg) ->
+ BinMsg = term_to_binary(Msg),
+
+ Msg = erlang:port_call(Prt, 0, Msg),
+ [{trace, Prt, 'receive', {S, {call, {0, BinMsg}}}},
+ {trace, Prt, send, {Prt, {call, BinMsg}}, S}] = flush()
+ end,
- Msg = erlang:port_call(Prt, 0, Msg),
- [{trace, Prt, 'receive', {S, {call, {0, BinMsg}}}},
- {trace, Prt, send, {Prt, {call, BinMsg}}, S}] = flush(),
+ Test({hello, world, make_ref()}),
+ Test({hello, world, lists:seq(1,1000)}),
close(Prt, Flags),
diff --git a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c
index b5728bc170..b5ae9389b4 100644
--- a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c
+++ b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c
@@ -217,6 +217,8 @@ static ErlDrvSSizeT echo_drv_control(ErlDrvData drv_data,
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen)
{
+ if ((len - 1) > rlen)
+ *rbuf = driver_alloc(len - 1);
memcpy(*rbuf, buf+1, len-1);
return len-1;
}
@@ -232,6 +234,8 @@ static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data,
char **rbuf, ErlDrvSizeT rlen,
unsigned int *flags)
{
+ if ((len - command) > rlen)
+ *rbuf = driver_alloc(len - command);
memcpy(*rbuf, buf+command, len-command);
return len-command;
}
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 61a68f9759..eaa4026a8a 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -46,7 +46,7 @@
process_status_exiting/1,
otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1,
process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1,
- spawn_opt_heap_size/1,
+ spawn_opt_heap_size/1, spawn_opt_max_heap_size/1,
processes_large_tab/1, processes_default_tab/1, processes_small_tab/1,
processes_this_tab/1, processes_apply_trap/1,
processes_last_call_trap/1, processes_gc_trap/1,
@@ -60,7 +60,7 @@
system_task_on_suspended/1,
gc_request_when_gc_disabled/1,
gc_request_blast_when_gc_disabled/1]).
--export([prio_server/2, prio_client/2]).
+-export([prio_server/2, prio_client/2, init/1, handle_event/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -84,7 +84,8 @@ all() ->
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
- spawn_opt_heap_size, otp_6237, {group, processes_bif},
+ spawn_opt_heap_size, spawn_opt_max_heap_size, otp_6237,
+ {group, processes_bif},
{group, otp_7738}, garb_other_running,
{group, system_task}].
@@ -425,6 +426,8 @@ t_process_info(Config) when is_list(Config) ->
{status, running} = process_info(self(), status),
{min_heap_size, 233} = process_info(self(), min_heap_size),
{min_bin_vheap_size,46422} = process_info(self(), min_bin_vheap_size),
+ {max_heap_size, #{ size := 0, kill := true, error_logger := true}} =
+ process_info(self(), max_heap_size),
{current_function,{?MODULE,t_process_info,1}} =
process_info(self(), current_function),
{current_function,{?MODULE,t_process_info,1}} =
@@ -564,6 +567,8 @@ process_info_other_msg(Config) when is_list(Config) ->
{min_heap_size, 233} = process_info(Pid, min_heap_size),
{min_bin_vheap_size, 46422} = process_info(Pid, min_bin_vheap_size),
+ {max_heap_size, #{ size := 0, kill := true, error_logger := true}} =
+ process_info(self(), max_heap_size),
Pid ! stop,
ok.
@@ -914,10 +919,14 @@ process_info_garbage_collection(_Config) ->
Parent = self(),
Pid = spawn_link(
fun() ->
+ %% We set mqd to off_heap and send an tuple
+ %% to process in order to force mbuf_size
+ %% to be used
+ process_flag(message_queue_data, off_heap),
receive go -> ok end,
(fun F(0) ->
Parent ! deep,
- receive ok -> ok end,
+ receive {ok,_} -> ok end,
[];
F(N) ->
timer:sleep(1),
@@ -926,31 +935,52 @@ process_info_garbage_collection(_Config) ->
Parent ! shallow,
receive done -> ok end
end),
- {garbage_collection_info, Before} =
- erlang:process_info(Pid, garbage_collection_info),
+ [{garbage_collection_info, Before},{total_heap_size, THSBefore}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
Pid ! go, receive deep -> ok end,
- {_, Deep} = erlang:process_info(Pid, garbage_collection_info),
- Pid ! ok, receive shallow -> ok end,
- {_, After} = erlang:process_info(Pid, garbage_collection_info),
+ [{_, Deep},{_,THSDeep}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
+ Pid ! {ok, make_ref()}, receive shallow -> ok end,
+ [{_, After},{_, THSAfter}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
Pid ! done,
%% Do some general checks to see if everything seems to be roughly correct
ct:log("Before: ~p",[Before]),
ct:log("Deep: ~p",[Deep]),
ct:log("After: ~p",[After]),
+ ct:log("Before THS: ~p",[THSBefore]),
+ ct:log("Deep THS: ~p",[THSDeep]),
+ ct:log("After THS: ~p",[THSAfter]),
%% Check stack_size
- true = proplists:get_value(stack_size, Before) < proplists:get_value(stack_size, Deep),
- true = proplists:get_value(stack_size, After) < proplists:get_value(stack_size, Deep),
+ true = gv(stack_size, Before) < gv(stack_size, Deep),
+ true = gv(stack_size, After) < gv(stack_size, Deep),
%% Check used heap size
- true = proplists:get_value(heap_size, Before) + proplists:get_value(old_heap_size, Before)
- < proplists:get_value(heap_size, Deep) + proplists:get_value(old_heap_size, Deep),
- true = proplists:get_value(heap_size, Before) + proplists:get_value(old_heap_size, Before)
- < proplists:get_value(heap_size, After) + proplists:get_value(old_heap_size, After),
+ true = gv(heap_size, Before) + gv(old_heap_size, Before)
+ < gv(heap_size, Deep) + gv(old_heap_size, Deep),
+ true = gv(heap_size, Before) + gv(old_heap_size, Before)
+ < gv(heap_size, After) + gv(old_heap_size, After),
+
+ %% Check that total_heap_size == heap_block_size + old_heap_block_size + mbuf_size
+ THSBefore = gv(heap_block_size, Before)
+ + gv(old_heap_block_size, Before)
+ + gv(mbuf_size, Before),
+
+ THSDeep = gv(heap_block_size, Deep)
+ + gv(old_heap_block_size, Deep)
+ + gv(mbuf_size, Deep),
+
+ THSAfter = gv(heap_block_size, After)
+ + gv(old_heap_block_size, After)
+ + gv(mbuf_size, After),
ok.
+gv(Key,List) ->
+ proplists:get_value(Key,List).
+
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
erlang:garbage_collect(),
@@ -1323,6 +1353,28 @@ process_flag_badarg(Config) when is_list(Config) ->
chk_badarg(fun () -> process_flag(min_heap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end),
+
+ chk_badarg(fun () -> process_flag(max_heap_size, gurka) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, -1) end),
+ chk_badarg(fun () ->
+ {_,Min} = process_info(self(), min_heap_size),
+ process_flag(max_heap_size, Min - 1)
+ end),
+ chk_badarg(fun () ->
+ {_,Min} = process_info(self(), min_heap_size),
+ process_flag(max_heap_size, #{size => Min - 1})
+ end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{}) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ kill => true }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ kill => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ error_logger => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ kill => true,
+ error_logger => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 1 bsl 64 }) end),
+
chk_badarg(fun () -> process_flag(priority, 4711) end),
chk_badarg(fun () -> process_flag(save_calls, hmmm) end),
P= spawn_link(fun () -> receive die -> ok end end),
@@ -1923,6 +1975,110 @@ spawn_opt_heap_size(Config) when is_list(Config) ->
Pid ! stop,
ok.
+spawn_opt_max_heap_size(_Config) ->
+
+ error_logger:add_report_handler(?MODULE, self()),
+
+ %% Test that numerical limit works
+ max_heap_size_test(1024, 1024, true, true),
+
+ %% Test that map limit works
+ max_heap_size_test(#{ size => 1024 }, 1024, true, true),
+
+ %% Test that no kill is sent
+ max_heap_size_test(#{ size => 1024, kill => false }, 1024, false, true),
+
+ %% Test that no error_logger report is sent
+ max_heap_size_test(#{ size => 1024, error_logger => false }, 1024, true, false),
+
+ %% Test that system_flag works
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => false,
+ error_logger => true}),
+ max_heap_size_test(#{ size => 1024 }, 1024, false, true),
+ max_heap_size_test(#{ size => 1024, kill => true }, 1024, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => true,
+ error_logger => false}),
+ max_heap_size_test(#{ size => 1024 }, 1024, true, false),
+ max_heap_size_test(#{ size => 1024, error_logger => true }, 1024, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 1 bsl 20, kill => true,
+ error_logger => true}),
+ max_heap_size_test(#{ }, 1 bsl 20, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => true,
+ error_logger => true}),
+
+ %% Test that ordinary case works as expected again
+ max_heap_size_test(1024, 1024, true, true),
+
+ ok.
+
+max_heap_size_test(Option, Size, Kill, ErrorLogger)
+ when map_size(Option) == 0 ->
+ max_heap_size_test([], Size, Kill, ErrorLogger);
+max_heap_size_test(Option, Size, Kill, ErrorLogger)
+ when is_map(Option); is_integer(Option) ->
+ max_heap_size_test([{max_heap_size, Option}], Size, Kill, ErrorLogger);
+max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
+ OomFun = fun F() -> timer:sleep(5),[lists:seq(1,1000)|F()] end,
+ Pid = spawn_opt(OomFun, Option),
+ {max_heap_size, MHSz} = erlang:process_info(Pid, max_heap_size),
+ ct:log("Default: ~p~nOption: ~p~nProc: ~p~n",
+ [erlang:system_info(max_heap_size), Option, MHSz]),
+
+ #{ size := Size} = MHSz,
+
+ Ref = erlang:monitor(process, Pid),
+ if Kill ->
+ receive
+ {'DOWN', Ref, process, Pid, killed} ->
+ ok
+ end;
+ true ->
+ ok
+ end,
+ if ErrorLogger ->
+ receive
+ {error, _, {emulator, _, [Pid|_]}} ->
+ ok
+ end;
+ true ->
+ ok
+ end,
+ if not Kill ->
+ exit(Pid, die),
+ receive
+ {'DOWN', Ref, process, Pid, die} ->
+ ok
+ end,
+ flush();
+ true ->
+ ok
+ end,
+ receive
+ M ->
+ ct:fail({unexpected_message, M})
+ after 10 ->
+ ok
+ end.
+
+flush() ->
+ receive
+ _M ->
+ flush()
+ after 1000 ->
+ ok
+ end.
+
+%% error_logger report handler proxy
+init(Pid) ->
+ {ok, Pid}.
+
+handle_event(Event, Pid) ->
+ Pid ! Event,
+ {ok, Pid}.
+
processes_term_proc_list(Config) when is_list(Config) ->
Tester = self(),
as_expected = processes_term_proc_list_test(false),
diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl
index af1a0d35d6..aae7651f6d 100644
--- a/erts/emulator/test/save_calls_SUITE.erl
+++ b/erts/emulator/test/save_calls_SUITE.erl
@@ -60,7 +60,7 @@ dont_break_reductions(Config) when is_list(Config) ->
RPS1 = reds_per_sched(0),
RPS2 = reds_per_sched(20),
Diff = abs(RPS1 - RPS2),
- true = (Diff < (0.05 * RPS1)),
+ true = (Diff < (0.2 * RPS1)),
ok.
diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl
index b7ff4c109c..c3e303bbd1 100644
--- a/erts/emulator/test/sensitive_SUITE.erl
+++ b/erts/emulator/test/sensitive_SUITE.erl
@@ -311,7 +311,7 @@ gc_trace(Config) when is_list(Config) ->
wait_trace(Self),
{messages,Messages} = process_info(Tracer, messages),
- [{trace,Self,gc_start,_},{trace,Self,gc_end,_}] = Messages,
+ [{trace,Self,gc_major_start,_},{trace,Self,gc_major_end,_}] = Messages,
unlink(Tracer), exit(Tracer, kill),
ok.
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index b7f312635e..da6a6bdea4 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -27,11 +27,12 @@
-export([all/0, suite/0, link_receive_call_correlation/0,
receive_trace/1, link_receive_call_correlation/1, self_send/1,
timeout_trace/1, send_trace/1,
- procs_trace/1, dist_procs_trace/1,
+ procs_trace/1, dist_procs_trace/1, procs_new_trace/1,
suspend/1, mutual_suspend/1, suspend_exit/1, suspender_exit/1,
suspend_system_limit/1, suspend_opts/1, suspend_waiting/1,
new_clear/1, existing_clear/1,
set_on_spawn/1, set_on_first_spawn/1, cpu_timestamp/1,
+ set_on_link/1, set_on_first_link/1,
system_monitor_args/1, more_system_monitor_args/1,
system_monitor_long_gc_1/1, system_monitor_long_gc_2/1,
system_monitor_large_heap_1/1, system_monitor_large_heap_2/1,
@@ -54,7 +55,8 @@ all() ->
mutual_suspend, suspend_exit, suspender_exit,
suspend_system_limit, suspend_opts, suspend_waiting,
new_clear, existing_clear, set_on_spawn,
- set_on_first_spawn, system_monitor_args,
+ set_on_first_spawn, set_on_link, set_on_first_link,
+ system_monitor_args,
more_system_monitor_args, system_monitor_long_gc_1,
system_monitor_long_gc_2, system_monitor_large_heap_1,
system_monitor_long_schedule,
@@ -82,7 +84,6 @@ cpu_timestamp(Config) when is_list(Config) ->
receive_trace(Config) when is_list(Config) ->
Receiver = fun_spawn(fun receiver/0),
- process_flag(trap_exit, true),
%% Trace the process; make sure that we receive the trace messages.
1 = erlang:trace(Receiver, true, ['receive']),
@@ -94,15 +95,116 @@ receive_trace(Config) when is_list(Config) ->
{trace, Receiver, 'receive', Hello2} = receive_first_trace(),
receive_nothing(),
+ %% Test 'receive' with matchspec
+ F1 = fun ({Pat, IsMatching}) ->
+ set_trace_pattern('receive', Pat, []),
+ Receiver ! Hello,
+ case IsMatching of
+ true ->
+ {trace, Receiver, 'receive', Hello} = receive_first_trace();
+ false ->
+ ok
+ end,
+ receive_nothing()
+ end,
+ From = self(),
+ Node = node(),
+ lists:foreach(F1, [{no, true},
+ {[{[Node, undefined,"Unexpected"],[],[]}], false},
+ {[{[Node, From,'_'],[],[]}], true},
+ {[{[Node, '$1','_'],[{'=/=','$1',From}],[]}], false},
+ {[{['$1', '_','_'],[{'=:=','$1',Node}],[]}], true},
+ {false, false},
+ {true, true}]),
+
+ %% Remote messages
+ OtherName = atom_to_list(?MODULE)++"_receive_trace",
+ {ok, OtherNode} = start_node(OtherName),
+ RemoteProc = spawn_link(OtherNode, ?MODULE, process, [self()]),
+ io:format("RemoteProc = ~p ~n", [RemoteProc]),
+
+ RemoteProc ! {send_please, Receiver, Hello},
+ {trace, Receiver, 'receive', Hello} = receive_first_trace(),
+ RemoteProc ! {send_please, Receiver, 99},
+ {trace, Receiver, 'receive', 99} = receive_first_trace(),
+
+ %% Remote with matchspec
+ F2 = fun (To, {Pat, IsMatching}) ->
+ set_trace_pattern('receive', Pat, []),
+ RemoteProc ! {send_please, To, Hello},
+ case IsMatching of
+ true ->
+ {trace, Receiver, 'receive', Hello} = receive_first_trace();
+ false ->
+ ok
+ end,
+ receive_nothing()
+ end,
+ F2(Receiver, {no, true}),
+ F2(Receiver, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}),
+ F2(Receiver, {[{[OtherNode, RemoteProc,'_'],[],[]},
+ {[OtherNode, undefined,'_'],[],[]}], true}),
+ F2(Receiver, {[{[OtherNode, '$1','_'],
+ [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}],
+ []}], true}),
+ F2(Receiver, {[{['$1', '_','_'], [{'=:=','$1',OtherNode}], []}], true}),
+ F2(Receiver, {false, false}),
+ F2(Receiver, {true, true}),
+
+ %% Remote to named with matchspec
+ Name = trace_SUITE_receiver,
+ register(Name, Receiver),
+ NN = {Name, node()},
+ F2(NN, {no, true}),
+ F2(NN, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}),
+ F2(NN, {[{[OtherNode, RemoteProc,'_'],[],[]},
+ {[OtherNode, undefined,'_'],[],[]}], true}),
+ F2(NN, {[{[OtherNode, '$1','_'],
+ [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}],
+ []}], true}),
+ F2(NN, {[{['$1', '_','_'], [{'==','$1',OtherNode}], []}], true}),
+ F2(NN, {false, false}),
+ F2(NN, {true, true}),
+
+ unlink(RemoteProc),
+ true = stop_node(OtherNode),
+
+ %% Timeout
+ Receiver ! {set_timeout, 10},
+ {trace, Receiver, 'receive', {set_timeout, 10}} = receive_first_trace(),
+ {trace, Receiver, 'receive', timeout} = receive_first_trace(),
+ erlang:trace_pattern('receive', [{[clock_service,undefined,timeout], [], []}], []),
+ Receiver ! {set_timeout, 7},
+ {trace, Receiver, 'receive', timeout} = receive_first_trace(),
+ erlang:trace_pattern('receive', true, []),
+
%% Another process should not be able to trace Receiver.
+ process_flag(trap_exit, true),
Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end),
{'EXIT', Intruder, {badarg, _}} = receive_first(),
%% Untrace the process; we should not receive anything.
- 1 = erlang:trace(Receiver, false, ['receive']),
- Receiver ! {hello, there},
- Receiver ! any_garbage,
- receive_nothing(),
+ ?line 1 = erlang:trace(Receiver, false, ['receive']),
+ ?line Receiver ! {hello, there},
+ ?line Receiver ! any_garbage,
+ ?line receive_nothing(),
+
+ %% Verify restrictions in matchspec for 'receive'
+ F3 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end,
+ WC = ['_','_','_'],
+ F3([{WC,[],[{message, {process_dump}}]}]),
+ F3([{WC,[{is_seq_trace}],[]}]),
+ F3([{WC,[],[{set_seq_token,label,4711}]}]),
+ F3([{WC,[],[{get_seq_token}]}]),
+ F3([{WC,[],[{enable_trace,call}]}]),
+ F3([{WC,[],[{enable_trace,self(),call}]}]),
+ F3([{WC,[],[{disable_trace,call}]}]),
+ F3([{WC,[],[{disable_trace,self(),call}]}]),
+ F3([{WC,[],[{trace,[call],[]}]}]),
+ F3([{WC,[],[{trace,self(),[],[call]}]}]),
+ F3([{WC,[],[{caller}]}]),
+ F3([{WC,[],[{silent,true}]}]),
+
ok.
%% Tests that receive of a message always happens before a call with
@@ -255,30 +357,111 @@ send_trace(Config) when is_list(Config) ->
%% Check that a message sent to another process is traced.
1 = erlang:trace(Sender, true, [send]),
+ F1 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F1, [no,
+ [{[Receiver,to_receiver],[],[]}],
+ [{['_','_'],[],[]}],
+ [{['$1','_'],[{is_pid,'$1'}],[]}],
+ [{['_','$1'],[{is_atom,'$1'}],[]}],
+ true]),
+
+ %% Test {message, Msg}
+ F1m = fun ({Pat, Msg}) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ {trace, Sender, send, to_receiver, Receiver, Msg} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F1m, [{[{['_','_'],[],[{message, 4711}]}], 4711},
+ {[{['_','_'],[],[{message, "4711"}]}], "4711"}
+ ]),
+
+ %% Test {message, {process_dump}}
+ set_trace_pattern(send, [{['_','_'],[],[{message, {process_dump}}]}], []),
Sender ! {send_please, Receiver, to_receiver},
- {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(),
+ {trace, Sender, send, to_receiver, Receiver, ProcDump} = receive_first_trace(),
+ true = is_binary(ProcDump),
receive_nothing(),
+ %% Same test with false match spec
+ F2 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ receive_nothing()
+ end,
+ lists:foreach(F2, [[{[Sender,to_receiver],[],[]}],
+ [{[Receiver,nomatch],[],[]}],
+ [{['$1','_'],[{is_atom,'$1'}],[]}],
+ [{['_','$1'],[{is_pid,'$1'}],[]}],
+ false,
+ [{['_','_'],[],[{message,false}]}],
+ [{['_','_'],[],[{silent,true}]}]]),
+ erlang:trace_pattern(send, true, []),
+ erlang:trace(Sender, false, [silent]),
+
%% Check that a message sent to another registered process is traced.
register(?MODULE,Receiver),
- Sender ! {send_please, ?MODULE, to_receiver},
- {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(),
- receive_nothing(),
+ F3 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, ?MODULE, to_receiver},
+ {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F3, [no,
+ [{[?MODULE,to_receiver],[],[]}],
+ [{['_','_'],[],[]}],
+ [{['$1','_'],[{is_atom,'$1'}],[]}],
+ [{['_','$1'],[{is_atom,'$1'}],[]}],
+ true]),
+ %% Again with false match spec
+ F4 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, ?MODULE, to_receiver},
+ receive_nothing()
+ end,
+ lists:foreach(F4, [[{[nomatch,to_receiver],[],[]}],
+ [{[?MODULE,nomatch],[],[]}],
+ [{['$1','_'],[{is_pid,'$1'}],[]}],
+ [{['_','$1'],[{is_pid,'$1'}],[]}],
+ [{['_','_'],[],[{message,false}]}],
+ [{['_','_'],[],[{silent,true}]}]
+ ]),
unregister(?MODULE),
+ erlang:trace_pattern(send, true, []),
+ erlang:trace(Sender, false, [silent]),
%% Check that a message sent to this process is traced.
- Sender ! {send_please, self(), to_myself},
- receive to_myself -> ok end,
- Self = self(),
- {trace, Sender, send, to_myself, Self} = receive_first_trace(),
- receive_nothing(),
+ F5 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, self(), to_myself},
+ receive to_myself -> ok end,
+ Self = self(),
+ {trace, Sender, send, to_myself, Self} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F5, [no,
+ [{[self(),to_myself],[],[]}],
+ [{['_','_'],[],[]}],
+ true]),
%% Check that a message sent to dead process is traced.
{Pid,Ref} = spawn_monitor(fun() -> ok end),
receive {'DOWN',Ref,_,_,_} -> ok end,
- Sender ! {send_please, Pid, to_dead},
- {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(),
- receive_nothing(),
+ F6 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Pid, to_dead},
+ {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F6, [no,
+ [{[Pid,to_dead],[],[]}],
+ [{['_','_'],[],[]}],
+ true]),
%% Check that a message sent to unknown registrated process is traced.
BadargSender = fun_spawn(fun sender/0),
@@ -299,8 +482,26 @@ send_trace(Config) when is_list(Config) ->
Sender ! {send_please, self(), to_myself_again},
receive to_myself_again -> ok end,
receive_nothing(),
+
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [global])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [local])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [meta])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [{meta,self()}])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_count])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_time])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, restart, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, pause, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, [{['_','_'],[],[{caller}]}], [])),
+
+ %% Done.
ok.
+set_trace_pattern(_, no, _) -> 0;
+set_trace_pattern(MFA, Pat, Flg) ->
+ R = erlang:trace_pattern(MFA, Pat, Flg),
+ {match_spec, Pat} = erlang:trace_info(MFA, match_spec),
+ R.
+
%% Test trace(Pid, How, [procs]).
procs_trace(Config) when is_list(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"),
@@ -436,6 +637,7 @@ dist_procs_trace(Config) when is_list(Config) ->
Proc2 ! {unlink_please, Proc1},
{trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(),
receive_nothing(),
+
%%
%% exit (with registered name, due to link)
Name = list_to_atom(OtherName),
@@ -454,6 +656,34 @@ dist_procs_trace(Config) when is_list(Config) ->
true = stop_node(OtherNode),
ok.
+%% Test trace(new, How, [procs]).
+procs_new_trace(Config) when is_list(Config) ->
+ Self = self(),
+ process_flag(trap_exit, true),
+ %%
+ Proc1 = spawn_link(?MODULE, process, [Self]),
+ io:format("Proc1 = ~p ~n", [Proc1]),
+ %%
+ 0 = erlang:trace(new, true, [procs]),
+
+ MFA = {?MODULE, process, [Self]},
+ %%
+ %% spawn, link
+ Proc1 ! {spawn_link_please, Self, MFA},
+ Proc3 = receive {spawned, Proc1, P3} -> P3 end,
+ receive {trace, Proc3, spawned, Proc1, MFA} -> ok end,
+ receive {trace, Proc3, getting_linked, Proc1} -> ok end,
+ io:format("Proc3 = ~p ~n", [Proc3]),
+ receive_nothing(),
+ %%
+ %%
+ %% exit (not linked to tracing process)
+ Reason1 = make_ref(),
+ Proc1 ! {exit_please, Reason1},
+ receive {'EXIT', Proc1, Reason1} -> ok end,
+ {trace, Proc3, exit, Reason1} = receive_first_trace(),
+ receive_nothing(),
+ ok.
@@ -501,6 +731,51 @@ set_on_first_spawn(Config) when is_list(Config) ->
receive_nothing(),
ok.
+%% Tests trace(Pid, How, [set_on_link]).
+
+set_on_link(Config) ->
+ Listener = fun_spawn(fun process/0),
+
+ %% Create and trace a process with the set_on_link flag.
+ %% Make sure it is traced.
+ Father_SOL = fun_spawn(fun process/0),
+ 1 = erlang:trace(Father_SOL, true, [send, set_on_link]),
+ true = is_send_traced(Father_SOL, Listener, sol_father),
+
+ %% Have the process spawn of two children and test that they
+ %% are traced.
+ [Child1, Child2] = spawn_children(Father_SOL, 2),
+ true = is_send_traced(Child1, Listener, child1),
+ true = is_send_traced(Child2, Listener, child2),
+
+ %% Second generation.
+ [Child11, Child12] = spawn_children(Child1, 2),
+ true = is_send_traced(Child11, Listener, child11),
+ true = is_send_traced(Child12, Listener, child12),
+ ok.
+
+%% Tests trace(Pid, How, [set_on_first_spawn]).
+
+set_on_first_link(Config) ->
+ ct:timetrap({seconds, 10}),
+ Listener = fun_spawn(fun process/0),
+
+ %% Create and trace a process with the set_on_first_spawn flag.
+ %% Make sure it is traced.
+ Parent = fun_spawn(fun process/0),
+ 1 = erlang:trace(Parent, true, [send, set_on_first_link]),
+ is_send_traced(Parent, Listener, sol_father),
+
+ %% Have the process spawn off three children and test that the
+ %% first is traced.
+ [Child1, Child2, Child3] = spawn_children(Parent, 3),
+ true = is_send_traced(Child1, Listener, child1),
+ false = is_send_traced(Child2, Listener, child2),
+ false = is_send_traced(Child3, Listener, child3),
+ receive_nothing(),
+ ok.
+
+
%% Tests arguments to erlang:system_monitor/0,1,2
system_monitor_args(Config) when is_list(Config) ->
@@ -1441,8 +1716,8 @@ receive_nothing() ->
receive
Any ->
ct:fail({unexpected_message, Any})
- after 200 ->
- ok
+ after 100 ->
+ ok
end.
@@ -1517,9 +1792,14 @@ sender() ->
%% Just consumes messages from its message queue.
receiver() ->
- receive
- _Any -> receiver()
- end.
+ receiver(infinity).
+
+receiver(Timeout) ->
+ receiver(receive
+ {set_timeout, NewTimeout} -> NewTimeout;
+ _Any -> Timeout
+ after Timeout -> infinity %% reset
+ end).
%% Works as long as it receives CPU time. Will always be RUNNABLE.
diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
index 1068c1d22d..a66563d15b 100644
--- a/erts/emulator/test/trace_port_SUITE.erl
+++ b/erts/emulator/test/trace_port_SUITE.erl
@@ -236,13 +236,13 @@ gc(Config) when is_list(Config) ->
trace_info(Garber, flags),
Garber ! hi,
- expect({trace,Garber,gc_start,info}),
- expect({trace,Garber,gc_end,info}),
+ expect({trace,Garber,gc_major_start,info}),
+ expect({trace,Garber,gc_major_end,info}),
trac(Garber, true, [garbage_collection,timestamp]),
Garber ! hi,
- expect({trace_ts,Garber,gc_start,info,ts}),
- expect({trace_ts,Garber,gc_end,info,ts}),
+ expect({trace_ts,Garber,gc_major_start,info,ts}),
+ expect({trace_ts,Garber,gc_major_end,info,ts}),
ok.
diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl
index 812e834562..de44d6656a 100644
--- a/erts/emulator/test/tracer_SUITE.erl
+++ b/erts/emulator/test/tracer_SUITE.erl
@@ -468,12 +468,12 @@ gc_start(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {Pid, gc_start, State, Pid, _, undefined, Opts} = Msg,
+ {Pid, gc_major_start, State, Pid, _, undefined, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
- test(gc_start, garbage_collection, Tc, Expect, true).
+ test(gc_major_start, garbage_collection, Tc, Expect, true).
gc_end(_Config) ->
@@ -488,12 +488,12 @@ gc_end(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {Pid, gc_end, State, Pid, _, undefined, Opts} = Msg,
+ {Pid, gc_major_end, State, Pid, _, undefined, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
- test(gc_end, garbage_collection, Tc, Expect, true).
+ test(gc_major_end, garbage_collection, Tc, Expect, true).
test(Event, Tc, Expect) ->
test(Event, Tc, Expect, true).
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 82a0303f86..7f4f78b5fe 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -150,6 +150,10 @@ static char *plush_val_switches[] = {
"ms",
"mbs",
"pds",
+ "max",
+ "maxk",
+ "maxel",
+ "mqd",
"",
NULL
};
@@ -160,12 +164,6 @@ static char *plusr_val_switches[] = {
NULL
};
-/* +x arguments with values */
-static char *plusx_val_switches[] = {
- "mqd",
- NULL
-};
-
/* +z arguments with values */
static char *plusz_val_switches[] = {
"dbbl",
@@ -986,20 +984,6 @@ int main(int argc, char **argv)
add_Eargs(argv[i+1]);
i++;
break;
- case 'x':
- if (!is_one_of_strings(&argv[i][2], plusx_val_switches)) {
- goto the_default;
- } else {
- if (i+1 >= argc
- || argv[i+1][0] == '-'
- || argv[i+1][0] == '+')
- usage(argv[i]);
- argv[i][0] = '-';
- add_Eargs(argv[i]);
- add_Eargs(argv[i+1]);
- i++;
- }
- break;
case 'z':
if (!is_one_of_strings(&argv[i][2], plusz_val_switches)) {
goto the_default;
@@ -1200,7 +1184,7 @@ usage_aux(void)
"[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] "
"[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] "
"[+T LEVEL] [+V] [+v] "
- "[+W<i|w|e>] [+x DEFAULT_PROC_FLAGS] [+z MISC_OPTION] [args ...]\n");
+ "[+W<i|w|e>] [+z MISC_OPTION] [args ...]\n");
exit(1);
}
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 77685013ae..de2693472a 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam
index ffe5d5631c..69804540c9 100644
--- a/erts/preloaded/ebin/erl_tracer.beam
+++ b/erts/preloaded/ebin/erl_tracer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 15e4ca82a7..8379bf1768 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam
index 3e95b1e42e..b1da0aa861 100644
--- a/erts/preloaded/ebin/erts_code_purger.beam
+++ b/erts/preloaded/ebin/erts_code_purger.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 53dd2897de..cc4f3dbdaf 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index b13b33170d..7b5797e90a 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam
index 51a412b7f7..f893b9c181 100644
--- a/erts/preloaded/ebin/otp_ring0.beam
+++ b/erts/preloaded/ebin/otp_ring0.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 45bed5cf7e..e4ce601c03 100644
--- a/erts/preloaded/ebin/prim_eval.beam
+++ b/erts/preloaded/ebin/prim_eval.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index c25bffb6a5..babba3081f 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 85d74de065..0521060e34 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index 1f8fe6f03b..e1faca7d96 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 059585ec99..5d3cbc1b36 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl
index 2177e48f60..de1e9ca01e 100644
--- a/erts/preloaded/src/erl_tracer.erl
+++ b/erts/preloaded/src/erl_tracer.erl
@@ -3,12 +3,29 @@
-export([enabled/3, trace/6, on_load/0]).
-type tracee() :: port() | pid() | undefined.
--type trace_tag() :: send | send_to_non_existing_process | 'receive' |
- call | return_to | return_from | exception_from |
- spawn | spawned | exit | link | unlink | getting_linked |
- getting_unlinked | register | unregister | in | out |
- in_exiting | out_exiting | out_exited |
- open | closed | gc_start | gc_end.
+
+-type trace_tag_running_ports() :: in | out | in_exiting | out_exiting | out_exited.
+-type trace_tag_running_procs() :: in | out | in_exiting | out_exiting | out_exited.
+-type trace_tag_send() :: send | send_to_non_existing_process.
+-type trace_tag_receive() :: 'receive'.
+-type trace_tag_call() :: call | return_to | return_from | exception_from.
+-type trace_tag_procs() :: spawn | spawned | exit | link | unlink
+ | getting_linked | getting_unlinked
+ | register | unregister.
+-type trace_tag_ports() :: open | closed | link | unlink
+ | getting_linked | getting_unlinked.
+-type trace_tag_gc() :: gc_minor_start | gc_minor_end
+ | gc_major_start | gc_major_end.
+
+-type trace_tag() :: trace_tag_send()
+ | trace_tag_receive()
+ | trace_tag_call()
+ | trace_tag_procs()
+ | trace_tag_ports()
+ | trace_tag_running_procs()
+ | trace_tag_running_ports()
+ | trace_tag_gc().
+
-type trace_opts() :: #{ match_spec_result => true | term(),
scheduler_id => undefined | non_neg_integer(),
timestamp => undefined | timestamp | cpu_timestamp |
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 3cc17014ff..3d152c4e92 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -1749,16 +1749,16 @@ trace_delivered(_Tracee) ->
erlang:nif_error(undefined).
%% trace_info/2
--spec erlang:trace_info(PidPortOrFunc, Item) -> Res when
- PidPortOrFunc :: pid() | port() | new | new_processes | new_ports
- | {Module, Function, Arity} | on_load,
+-spec erlang:trace_info(PidPortFuncEvent, Item) -> Res when
+ PidPortFuncEvent :: pid() | port() | new | new_processes | new_ports
+ | {Module, Function, Arity} | on_load | send | 'receive',
Module :: module(),
Function :: atom(),
Arity :: arity(),
Item :: flags | tracer | traced | match_spec
| meta | meta_match_spec | call_count | call_time | all,
Res :: trace_info_return().
-trace_info(_PidPortOrFunc, _Item) ->
+trace_info(_PidPortFuncEvent, _Item) ->
erlang:nif_error(undefined).
%% trunc/1
@@ -2073,6 +2073,9 @@ open_port(PortName, PortSettings) ->
(min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when
MinBinVHeapSize :: non_neg_integer(),
OldMinBinVHeapSize :: non_neg_integer();
+ (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when
+ MaxHeapSize :: max_heap_size(),
+ OldMaxHeapSize :: max_heap_size();
(message_queue_data, MQD) -> OldMQD when
MQD :: message_queue_data(),
OldMQD :: message_queue_data();
@@ -2154,6 +2157,7 @@ process_flag(_Flag, _Value) ->
{messages, MessageQueue :: [term()]} |
{min_heap_size, MinHeapSize :: non_neg_integer()} |
{min_bin_vheap_size, MinBinVHeapSize :: non_neg_integer()} |
+ {max_heap_size, MaxHeapSize :: max_heap_size()} |
{monitored_by, Pids :: [pid()]} |
{monitors,
Monitors :: [{process, Pid :: pid() |
@@ -2238,6 +2242,7 @@ setelement(_Index, _Tuple1, _Value) ->
| {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
+ | {max_heap_size, Size :: max_heap_size()}
| {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(_Tuple) ->
erlang:nif_error(undefined).
@@ -2257,9 +2262,9 @@ spawn_opt(_Tuple) ->
Input :: non_neg_integer(),
Output :: non_neg_integer();
(microstate_accounting) -> [MSAcc_Thread] | undefined when
- MSAcc_Thread :: #{ type => MSAcc_Thread_Type,
- id => MSAcc_Thread_Id,
- counters => MSAcc_Counters},
+ MSAcc_Thread :: #{ type := MSAcc_Thread_Type,
+ id := MSAcc_Thread_Id,
+ counters := MSAcc_Counters},
MSAcc_Thread_Type :: scheduler | async | aux,
MSAcc_Thread_Id :: non_neg_integer(),
MSAcc_Counters :: #{ MSAcc_Thread_State => non_neg_integer() },
@@ -2330,6 +2335,9 @@ subtract(_,_) ->
OldMinBinVHeapSize when
MinBinVHeapSize :: non_neg_integer(),
OldMinBinVHeapSize :: non_neg_integer();
+ (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when
+ MaxHeapSize :: max_heap_size(),
+ OldMaxHeapSize :: max_heap_size();
(multi_scheduling, BlockState) -> OldBlockState when
BlockState :: block | unblock | block_normal | unblock_normal,
OldBlockState :: blocked | disabled | enabled;
@@ -2381,7 +2389,7 @@ tl(_List) ->
[{[term()] | '_' ,[term()],[term()]}].
-spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when
- MFA :: trace_pattern_mfa(),
+ MFA :: trace_pattern_mfa() | send | 'receive',
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
| restart
@@ -2403,7 +2411,13 @@ trace_pattern(MFA, MatchSpec) ->
call_count |
call_time.
--spec erlang:trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
+-spec erlang:trace_pattern(send, MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ ('receive', MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ (MFA, MatchSpec, FlagList) -> non_neg_integer() when
MFA :: trace_pattern_mfa(),
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
@@ -2505,6 +2519,7 @@ tuple_to_list(_Tuple) ->
logical_processors_available |
logical_processors_online) -> unknown | pos_integer();
(machine) -> string();
+ (max_heap_size) -> {max_heap_size, MaxHeapSize :: max_heap_size()};
(message_queue_data) -> message_queue_data();
(min_heap_size) -> {min_heap_size, MinHeapSize :: pos_integer()};
(min_bin_vheap_size) -> {min_bin_vheap_size,
@@ -2642,6 +2657,13 @@ spawn_monitor(M, F, A) ->
erlang:error(badarg, [M,F,A]).
+-type max_heap_size() ::
+ Size :: non_neg_integer()
+ %% TODO change size => to := when -type maps support is finalized
+ | #{ size => non_neg_integer(),
+ kill => boolean(),
+ error_logger => boolean() }.
+
-type spawn_opt_option() ::
link
| monitor
@@ -2649,6 +2671,7 @@ spawn_monitor(M, F, A) ->
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
| {min_bin_vheap_size, VSize :: non_neg_integer()}
+ | {max_heap_size, Size :: max_heap_size()}
| {message_queue_data, MQD :: message_queue_data()}.
-spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 77684751c8..618b53f6bb 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -41,6 +41,7 @@
%% -s : Start own processes.
%%
%% Experimental flags:
+%% -profile_boot : Use an 'eprof light' to profile boot sequence
%% -init_debug : Activate debug printouts in init
%% -loader_debug : Activate debug printouts in erl_prim_loader
%% -code_path_choice : strict | relaxed
@@ -184,6 +185,11 @@ boot(BootArgs) ->
erl_tracer:on_load(),
{Start0,Flags,Args} = parse_boot_args(BootArgs),
+ %% We don't get to profile parsing of BootArgs
+ case get_flag(profile_boot, Flags, false) of
+ false -> ok;
+ true -> debug_profile_start()
+ end,
Start = map(fun prepare_run_args/1, Start0),
boot(Start, Flags, Args).
@@ -765,7 +771,14 @@ do_boot(Init,Flags,Start) ->
%% print the node name into the Purify log.
(catch erlang:system_info({purify, "Node: " ++ atom_to_list(node())})),
- start_em(Start).
+ start_em(Start),
+ case get_flag(profile_boot,Flags,false) of
+ false -> ok;
+ true ->
+ debug_profile_format_mfas(debug_profile_mfas()),
+ debug_profile_stop()
+ end,
+ ok.
get_root(Flags) ->
case get_argument(root, Flags) of
@@ -1339,3 +1352,64 @@ run_on_load_handlers([M|Ms], Debug) ->
end
end;
run_on_load_handlers([], _) -> ok.
+
+
+%% debug profile (light variant of eprof)
+debug_profile_start() ->
+ _ = erlang:trace_pattern({'_','_','_'},true,[call_time]),
+ _ = erlang:trace_pattern(on_load,true,[call_time]),
+ _ = erlang:trace(all,true,[call]),
+ ok.
+
+debug_profile_stop() ->
+ _ = erlang:trace_pattern({'_','_','_'},false,[call_time]),
+ _ = erlang:trace_pattern(on_load,false,[call_time]),
+ _ = erlang:trace(all,false,[call]),
+ ok.
+
+debug_profile_mfas() ->
+ _ = erlang:trace_pattern({'_','_','_'},pause,[call_time]),
+ _ = erlang:trace_pattern(on_load,pause,[call_time]),
+ MFAs = collect_loaded_mfas() ++ erlang:system_info(snifs),
+ collect_mfas(MFAs,[]).
+
+%% debug_profile_format_mfas should be called at the end of the boot phase
+%% so all pertinent modules should be loaded at that point.
+debug_profile_format_mfas(MFAs0) ->
+ MFAs = lists:sort(MFAs0),
+ lists:foreach(fun({{Us,C},{M,F,A}}) ->
+ Str = io_lib:format("~w:~w/~w", [M,F,A]),
+ io:format(standard_error,"~55s - ~6w : ~w us~n", [Str,C,Us])
+ end, MFAs),
+ ok.
+
+collect_loaded_mfas() ->
+ Ms = [M || M <- [element(1, Mi) || Mi <- code:all_loaded()]],
+ collect_loaded_mfas(Ms,[]).
+
+collect_loaded_mfas([],MFAs) -> MFAs;
+collect_loaded_mfas([M|Ms],MFAs0) ->
+ MFAs = [{M,F,A} || {F,A} <- M:module_info(functions)],
+ collect_loaded_mfas(Ms,MFAs ++ MFAs0).
+
+
+collect_mfas([], Info) -> Info;
+collect_mfas([MFA|MFAs],Info) ->
+ case erlang:trace_info(MFA,call_time) of
+ {call_time, []} ->
+ collect_mfas(MFAs,Info);
+ {call_time, false} ->
+ collect_mfas(MFAs,Info);
+ {call_time, Data} ->
+ case collect_mfa(MFA,Data,0,0) of
+ {{0,_},_} ->
+ %% ignore mfas with zero time
+ collect_mfas(MFAs,Info);
+ MfaData ->
+ collect_mfas(MFAs,[MfaData|Info])
+ end
+ end.
+
+collect_mfa(Mfa,[],Count,Time) -> {{Time,Count},Mfa};
+collect_mfa(Mfa,[{_Pid,C,S,Us}|Data],Count,Time) ->
+ collect_mfa(Mfa,Data,Count + C,Time + S * 1000000 + Us).
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/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 37ec4e97c9..6dc162db40 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -135,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
]).
@@ -1694,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/core_pp.erl b/lib/compiler/src/core_pp.erl
index 88275998be..f34a5c034f 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -37,10 +37,11 @@
indent = 0 :: integer(),
item_indent = 2 :: integer(),
body_indent = 4 :: integer(),
- tab_width = 8 :: non_neg_integer(),
line = 0 :: integer(),
clean = true :: boolean()}).
+-define(TAB_WIDTH, 8).
+
-spec format(cerl:cerl()) -> iolist().
format(Node) ->
@@ -470,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) ->
@@ -509,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/v3_core.erl b/lib/compiler/src/v3_core.erl
index 3299149457..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;
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 f55ea9a3a6..e634f0fcc2 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -31,14 +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,
+ 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.
@@ -51,7 +49,7 @@ all() ->
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, encrypted_abstr,
strict_record,
- missing_testheap, cover, env, core, core_roundtrip, asm,
+ cover, env, core, core_roundtrip, asm,
sys_pre_attributes, dialyzer, warnings, pre_load_check].
groups() ->
@@ -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(),
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/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/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 66daf6bb0b..d68a78ed6d 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -1764,11 +1764,13 @@ An example return value with for a client service with Origin-Host
{send_pend,0}]}]},
{statistics,[{{{0,258,0},recv},3},
{{{0,258,1},send},3},
+ {{{0,258,0},recv,{'Result-Code',2001}},3},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
- {{{0,258,0},recv,{'Result-Code',2001}},3},
+ {{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,280,1},recv},2},
- {{{0,280,0},send},2}]}]]
+ {{{0,280,0},send},2},
+ {{{0,280,0},send,{'Result-Code',2001}},2}]}]]
</pre>
<p>
@@ -1844,13 +1846,16 @@ connection might look as follows.</p>
[{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
{statistics,[{{{0,280,0},recv},7},
{{{0,280,1},send},7},
- {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,280,0},recv,{'Result-Code',2001}},7},
{{{0,258,1},recv},3},
{{{0,258,0},send},3},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
{{{0,280,1},recv},5},
{{{0,280,0},send},5},
+ {{{0,280,0},send,{'Result-Code',2001}},5},
{{{0,257,1},recv},1},
- {{{0,257,0},send},1}]}]]
+ {{{0,257,0},send},1},
+ {{{0,257,0},send,{'Result-Code',2001}},1}]}]]
</pre>
<p>
@@ -1918,13 +1923,16 @@ A return value for the server above might look as follows.</p>
{send_pend,0}]}]},
{statistics,[{{{0,280,0},recv},62},
{{{0,280,1},send},62},
- {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,280,0},recv,{'Result-Code',2001}},62},
{{{0,258,1},recv},3},
{{{0,258,0},send},3},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
{{{0,280,1},recv},66},
{{{0,280,0},send},66},
+ {{{0,280,0},send,{'Result-Code',2001}},66},
{{{0,257,1},recv},1},
- {{{0,257,0},send},1}]}]]
+ {{{0,257,0},send},1},
+ {{{0,257,0},send,{'Result-Code',2001}},1}]}]]
</pre>
<p>
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/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 12b09889d1..618d5a7f10 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -47,10 +47,9 @@
{"1.9.2.2", [{restart_application, diameter}]}, %% 17.5.6.7
{"1.9.2.3", [{restart_application, diameter}]}, %% 17.5.6.8
{"1.10", [{restart_application, diameter}]}, %% 18.0
- {"1.11", [{load_module, diameter_traffic}, %% 18.1
- {update, diameter_service, {advanced, []}}]},
- {"1.11.1", [{load_module, diameter_traffic}, %% 18.2
- {update, diameter_service, {advanced, []}}]}
+ {"1.11", [{restart_application, diameter}]}, %% 18.1
+ {"1.11.1", [{restart_application, diameter}]}, %% 18.2
+ {"1.11.2", [{restart_application, diameter}]} %% 18.3
],
[
{"0.9", [{restart_application, diameter}]},
@@ -80,6 +79,7 @@
{"1.9.2.3", [{restart_application, diameter}]},
{"1.10", [{restart_application, diameter}]},
{"1.11", [{restart_application, diameter}]},
- {"1.11.1", [{restart_application, diameter}]}
+ {"1.11.1", [{restart_application, diameter}]},
+ {"1.11.2", [{restart_application, diameter}]}
]
}.
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/diameter/vsn.mk b/lib/diameter/vsn.mk
index 836def8447..cb750c69a3 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -1,6 +1,6 @@
# %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.
@@ -17,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.11.2
+DIAMETER_VSN = 1.12
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
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/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/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/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/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 204b8fa394..45da909264 100644
--- a/lib/mnesia/test/mnesia_config_test.erl
+++ b/lib/mnesia/test/mnesia_config_test.erl
@@ -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_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/odbc/configure.in b/lib/odbc/configure.in
index 2871d91a1c..cb2b23d534 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -73,6 +73,16 @@ AC_CHECK_TOOL(LD, ld, '$(CC)')
AC_SUBST(LD)
+_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
+
+AC_PATH_PROG(RM, rm, false, $_search_path)
+if test "$ac_cv_path_RM" = false; then
+ AC_MSG_ERROR([No 'rm' command found])
+fi
+
+_search_path=
+
+
# Sockets
#--------------------------------------------------------------------
# Check for the existence of the -lsocket and -lnsl libraries.
@@ -128,7 +138,7 @@ dnl Checks for library functions.
AC_CHECK_FUNCS([memset socket])
# ODBC
-/bin/rm -f "$ERL_TOP/lib/odbc/SKIP"
+$RM -f "$ERL_TOP/lib/odbc/SKIP"
LM_CHECK_THR_LIB
AC_SUBST(THR_DEFS)
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/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 9c9d6ca352..5a3c885571 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -22,6 +22,7 @@
%% Test functions
-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,
@@ -39,6 +40,7 @@ suite() ->
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,
@@ -151,6 +153,226 @@ message(Config) when is_list(Config) ->
{trace,S,call,{dbg,ln,[]},S}] = flush(),
ok.
+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) ->
{ok, _} = start(),
@@ -167,8 +389,8 @@ distributed(Config) when is_list(Config) ->
{value, {matched, Node, 1}} = lists:keysearch(Node, 2, Z),
dbg:cn(Node),
dbg:tp(dbg,ln,[]),
- ok = rpc:call(Node, dbg, ltp, []),
- ok = rpc:call(Node, dbg, ln, []),
+ ok = rpc:block_call(Node, dbg, ltp, []),
+ ok = rpc:block_call(Node, dbg, ln, []),
ok = dbg:ln(),
S = self(),
{TraceSend, TraceCall} =
@@ -447,12 +669,6 @@ file_port_schedfix1(Config) when is_list(Config) ->
%%
{ok,[{matched,_,_}]} = dbg:p(all, [clear]),
stop(),
- % Some debug code to run on all platforms, for finding the fault on genny
- % Dont touch please /PaN
- io:format("Trace dump by PaN BEGIN~n"),
- 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,
- io:format("Trace dump by PaN END~n"),
%%
%% Get the trace result
%%
@@ -473,14 +689,12 @@ file_port_schedfix1(Config) when is_list(Config) ->
%%
%% Analyze the 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),
+ {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
io:format("Min = ~f, Max = ~f~n",[Min,Max]),
%%
@@ -895,6 +1109,10 @@ schedstat_handler(TraceMsg, {Parent, Tag, Data} = State) ->
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
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/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 6e1d18cc95..d9be1a32b7 100644
--- a/lib/ssh/test/ssh_benchmark_SUITE.erl
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -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 cd6c5f82b9..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),
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 4db7d09ccd..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),
@@ -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
@@ -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 025e8cea61..dbbb25025c 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -696,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),
@@ -885,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)
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 544d9b41cd..dc0a0c2cc4 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -992,21 +992,21 @@ suite({ecdh_rsa, aes_256_cbc, sha384, sha384}) ->
%% RFC 5288 AES-GCM Cipher Suites
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, sha384}) ->
+suite({dhe_rsa, aes_128_gcm, null, sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
-suite({dhe_rsa, aes_256_gcm, null, sha256}) ->
+suite({dhe_rsa, aes_256_gcm, null, sha384}) ->
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
-suite({dh_rsa, aes_128_gcm, null, sha384}) ->
+suite({dh_rsa, aes_128_gcm, null, sha256}) ->
?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
-suite({dh_rsa, aes_256_gcm, null, sha256}) ->
+suite({dh_rsa, aes_256_gcm, null, sha384}) ->
?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
-suite({dhe_dss, aes_128_gcm, null, sha384}) ->
+suite({dhe_dss, aes_128_gcm, null, sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
-suite({dhe_dss, aes_256_gcm, null, sha256}) ->
+suite({dhe_dss, aes_256_gcm, null, sha384}) ->
?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
-suite({dh_dss, aes_128_gcm, null, sha384}) ->
+suite({dh_dss, aes_128_gcm, null, sha256}) ->
?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
suite({dh_dss, aes_256_gcm, null, sha384}) ->
?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 0073e86e26..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,10 +554,10 @@ 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,
@@ -504,16 +565,18 @@ certify(#server_hello_done{},
when Alg == rsa_psk ->
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,7 +1985,7 @@ 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.
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 2349158b11..60b4fbe995 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -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/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..f34eebb0e4 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -278,11 +278,13 @@ 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) ->
- ordsets:intersection(ClientHashSigns, SupportedHashSigns);
+ _, {Major, Minor}) when (Major >= 3) andalso (Minor >= 3) ->
+ sets:to_list(sets:intersection(sets:from_list(ClientHashSigns),
+ sets:from_list(SupportedHashSigns)));
available_signature_algs(_, _, _, _) ->
undefined.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 0c3c7aa39b..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() ->
@@ -325,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;
@@ -430,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"}].
@@ -719,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).
%%--------------------------------------------------------------------
@@ -764,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"}].
@@ -2556,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]),
@@ -2583,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),
@@ -3051,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}} =
@@ -3084,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).
@@ -3677,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_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index db9e1c3d38..38cc3532d8 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -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/epp.xml b/lib/stdlib/doc/src/epp.xml
index 0802f0e47c..ac87f9c2b6 100644
--- a/lib/stdlib/doc/src/epp.xml
+++ b/lib/stdlib/doc/src/epp.xml
@@ -102,6 +102,7 @@
<func>
<name name="parse_erl_form" arity="1"/>
<fsummary>Return the next Erlang form from the opened Erlang source file</fsummary>
+ <type name="warning_info"/>
<desc>
<p>Returns the next Erlang form from the opened Erlang source file.
The tuple <c>{eof, <anno>Line</anno>}</c> is returned at end-of-file. The first
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 245580b1ba..f02b1f0651 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -73,6 +73,13 @@
<name name="priority_level"/>
</datatype>
<datatype>
+ <name name="max_heap_size"/>
+ <desc>
+ <p>See <seealso marker="erts:erlang#process_flag_max_heap_size">
+ erlang:process_flag(max_heap_size, MaxHeapSize)</seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="dict_or_pid"/>
</datatype>
</datatypes>
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/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/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/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 3f79ed0f87..4a19603ec2 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -43,12 +43,19 @@
%%-----------------------------------------------------------------------------
-type priority_level() :: 'high' | 'low' | 'max' | 'normal'.
+-type max_heap_size() :: non_neg_integer() |
+ #{ size => non_neg_integer(),
+ kill => true,
+ error_logger => true}.
-type spawn_option() :: 'link'
| 'monitor'
| {'priority', priority_level()}
+ | {'max_heap_size', max_heap_size()}
| {'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 :: [_])
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/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/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/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/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/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/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 283e97f4e2..942baf7c7f 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -14630,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);
@@ -14644,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);
@@ -14665,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;
@@ -14679,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;
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_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/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl
index f88c10f987..6944a78360 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -643,8 +643,11 @@ modal(Config) ->
wx:set_env(Env),
modal_dialog(Frame, 1, Tester)
end),
- receive {dialog, M1, 1} -> timer:sleep(200), ets:insert(test_state, {M1, ready}) end,
- receive {dialog, M2, 2} -> timer:sleep(200), ets:insert(test_state, {M2, ready}) 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,
diff --git a/otp_build b/otp_build
index 57dc2e3a9c..1b79b0b0fe 100755
--- a/otp_build
+++ b/otp_build
@@ -139,7 +139,7 @@ check_erltop ()
if [ "X$ERL_TOP" = "X" ]; then
if [ -f ./otp_build -a -f ./erts/autoconf/config.guess ]; then
ERLTOP_FORCED=true
- ERL_TOP=`/bin/pwd`
+ ERL_TOP=`pwd`
export ERL_TOP
else
echo "The environment variable ERL_TOP must be set." >&2
@@ -679,7 +679,7 @@ echo_env_erltop ()
if [ X"$ERL_TOP" = X"" -o "$ERLTOP_FORCED" = "true" ]; then
if [ -f ./otp_build ]; then
# Seems to be current directory...
- echo_setenv ERL_TOP `/bin/pwd` ';'
+ echo_setenv ERL_TOP `pwd` ';'
else
echo "You need to either set ERL_TOP first or stand in the same" \
"directory as this script resides in." >&2
diff --git a/otp_versions.table b/otp_versions.table
index f339eab796..f68986d831 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,5 @@
+OTP-18.3.3 : common_test-1.12.1 inets-6.2.3 ssl-7.3.2 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
+OTP-18.3.2 : inets-6.2.2 ssl-7.3.1 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.1 : erts-7.3.1 inets-6.2.1 mnesia-4.13.4 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 ssl-7.3 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3 : asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosNotification-1.2.1 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3 eunit-2.2.13 hipe-3.15 inets-6.2 kernel-4.2 mnesia-4.13.3 observer-2.1.2 orber-3.8.1 public_key-1.1.1 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 ssl-7.3 stdlib-2.8 test_server-3.10 tools-2.8.3 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 # cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosProperty-1.2 et-1.5.1 gs-1.6 ic-4.4 jinterface-1.6.1 megaco-3.18 odbc-2.11.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 reltool-0.7 syntax_tools-1.7 typer-0.9.10 :
OTP-18.2.4 : common_test-1.11.2 # asn1-4.0.1 compiler-6.0.2 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2 cosProperty-1.2 cosTime-1.2 cosTransactions-1.3 crypto-3.6.2 debugger-4.1.1 dialyzer-2.8.2 diameter-1.11.1 edoc-0.7.17 eldap-1.2 erl_docgen-0.4.1 erl_interface-3.8.1 erts-7.2.1 et-1.5.1 eunit-2.2.12 gs-1.6 hipe-3.14 ic-4.4 inets-6.1.1 jinterface-1.6.1 kernel-4.1.1 megaco-3.18 mnesia-4.13.2 observer-2.1.1 odbc-2.11.1 orber-3.8 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1 reltool-0.7 runtime_tools-1.9.2 sasl-2.6.1 snmp-5.2.1 ssh-4.2.1 ssl-7.2 stdlib-2.7 syntax_tools-1.7 test_server-3.9.1 tools-2.8.2 typer-0.9.10 webtool-0.9 wx-1.6 xmerl-1.3.9 :
diff --git a/system/doc/reference_manual/code_loading.xml b/system/doc/reference_manual/code_loading.xml
index e9da40e02f..f6fd2911fa 100644
--- a/system/doc/reference_manual/code_loading.xml
+++ b/system/doc/reference_manual/code_loading.xml
@@ -130,26 +130,6 @@ loop() ->
<marker id="on_load"></marker>
<title>Running a Function When a Module is Loaded</title>
- <warning>
- <p>The <c>on_load</c> feature is to be considered experimental
- as there are a number of known weak points in current semantics,
- which therefore might change in future Erlang/OTP releases:</p>
- <list>
- <item><p>Doing external call in <c>on_load</c> to the module itself
- leads to deadlock.</p></item>
- <item><p>At module upgrade, other processes calling the module
- get suspended waiting for <c>on_load</c> to finish. This can be very bad
- for applications with demands on realtime characteristics.</p></item>
- <item><p>At module upgrade, no rollback is done if the
- <c>on_load</c> function fails.
- The system is left in a bad limbo state without any working
- and reachable instance of the module.</p></item>
- </list>
- <p>The problems with module upgrade described above can be fixed in future
- Erlang/OTP releases by changing the behaviour to not make the module reachable until
- after the <c>on_load</c> function has successfully returned.</p>
- </warning>
-
<p>The <c>-on_load()</c> directive names a function that is to
be run automatically when a module is loaded.</p>
<p>Its syntax is as follows:</p>
@@ -159,20 +139,35 @@ loop() ->
<p>It is not necessary to export the function. It is called in a
freshly spawned process (which terminates as soon as the function
- returns). The function must return <c>ok</c> if the module is to
- remain loaded and become callable, or any other value if the module
- is to be unloaded. Generating an exception also causes the
- module to be unloaded. If the return value is not an atom,
- a warning error report is sent to the error logger.</p>
-
- <p>A process that calls any function in a module whose <c>on_load</c>
- function has not yet returned, is suspended until the <c>on_load</c>
- function has returned.</p>
+ returns).</p>
+
+ <p>The function must return <c>ok</c> if the module is to
+ become the new current code for the module and become
+ callable.</p>
+
+ <p>Returning any other value or generating an exception
+ causes the new code to be unloaded. If the return value is not an
+ atom, a warning error report is sent to the error logger.</p>
+
+ <p>If there already is current code for the module, that code will
+ remain current and can be called until the <c>on_load</c> function
+ has returned. If the <c>on_load</c> function fails, the current
+ code (if any) will remain current. If there is no current code for
+ a module, any process that makes an external call to the module
+ before the <c>on_load</c> function has finished will be suspended
+ until the <c>on_load</c> function have finished.</p>
+
+ <note>
+ <p>Before OTP 19, if the <c>on_load</c> function failed, any
+ previously current code would become old, essentially leaving
+ the system without any working and reachable instance of the
+ module. That problem has been eliminated in OTP 19.</p>
+ </note>
<p>In embedded mode, first all modules are loaded.
Then all <c>on_load</c> functions are called. The system is
terminated unless all of the <c>on_load</c> functions return
- <c>ok</c></p>.
+ <c>ok</c>.</p>
<p><em>Example:</em></p>
diff --git a/system/doc/reference_manual/macros.xml b/system/doc/reference_manual/macros.xml
index 42ea639b54..350bb1d123 100644
--- a/system/doc/reference_manual/macros.xml
+++ b/system/doc/reference_manual/macros.xml
@@ -234,6 +234,53 @@ or
</section>
<section>
+ <title>-error() and -warning() directives</title>
+
+ <p>The directive <c>-error(Term)</c> causes a compilation error.</p>
+
+ <p><em>Example:</em></p>
+ <code type="none">
+-module(t).
+-export([version/0]).
+
+-ifdef(VERSION).
+version() -> ?VERSION.
+-else.
+-error("Macro VERSION must be defined.").
+version() -> "".
+-endif.</code>
+
+ <p>The error message will look like this:</p>
+
+ <pre>
+% <input>erlc t.erl</input>
+t.erl:7: -error("Macro VERSION must be defined.").</pre>
+
+ <p>The directive <c>-warning(Term)</c> causes a compilation warning.</p>
+
+ <p><em>Example:</em></p>
+ <code type="none">
+-module(t).
+-export([version/0]).
+
+-ifndef(VERSION).
+-warning("Macro VERSION not defined -- using default version.").
+-define(VERSION, "0").
+-endif.
+version() -> ?VERSION.</code>
+
+ <p>The warning message will look like this:</p>
+
+ <pre>
+% <input>erlc t.erl</input>
+t.erl:5: Warning: -warning("Macro VERSION not defined -- using default version.").</pre>
+
+ <p>The <c>-error()</c> and <c>-warning()</c> directives were added
+ in OTP 19.</p>
+
+ </section>
+
+ <section>
<title>Stringifying Macro Arguments</title>
<p>The construction <c>??Arg</c>, where <c>Arg</c> is a macro
argument, is expanded to a string containing the tokens of
@@ -253,5 +300,6 @@ io:format("Call ~s: ~w~n",["you : function ( 2 , 1 )",you:function(2,1)]).</code
<p>That is, a trace output, with both the function called and
the resulting value.</p>
</section>
+
</chapter>
diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml
index c5d24a96b5..9e26e9058d 100644
--- a/system/doc/reference_manual/typespec.xml
+++ b/system/doc/reference_manual/typespec.xml
@@ -132,15 +132,18 @@
| nonempty_list(Type) %% Proper non-empty list
Map :: map() %% stands for a map of any size
- | #{} %% stands for a map of any size
+ | #{} %% stands for the empty map
| #{PairList}
Tuple :: tuple() %% stands for a tuple of any size
| {}
| {TList}
- PairList :: Type => Type
- | Type => Type, PairList
+ PairList :: Pair
+ | Pair, PairList
+
+ Pair :: Type := Type %% notes a pair that must be present
+ | Type => Type
TList :: Type
| Type, TList
@@ -170,6 +173,23 @@
The notation <c>[]</c> specifies the singleton type for the empty list.
</p>
<p>
+ The general form of maps is <c>#{PairList}</c>. The key types in
+ <c>PairList</c> are allowed to overlap, and if they do, the leftmost pair
+ takes precedence. A map value does not belong to this type if contains a key
+ that is not in <c>PairList</c>.
+ </p>
+ <p>
+ Because it is common to end a map type with <c>any() =&gt; any()</c> to denote
+ that keys that do not belong to any other pair in <c>PairList</c> are
+ allowed, and may map to any value, the shorthand notation <c>...</c> is
+ allowed as the last pair of a map type.
+ </p>
+ <p>
+ Notice that the syntactic representation of <c>map()</c> is <c>#{...}</c>
+ (or <c>#{_ =&gt; _}</c>, or <c>#{any() =&gt; any()}</c>), not <c>#{}</c>.
+ The notation <c>#{}</c> specifies the singleton type for the empty map.
+ </p>
+ <p>
For convenience, the following types are also built-in.
They can be thought as predefined aliases for the type unions also shown in
the table.
@@ -302,12 +322,6 @@
This is described in <seealso marker="#typeinrecords">
Type Information in Record Declarations</seealso>.
</p>
- <note>
- <p>Map types, both <c>map()</c> and <c>#{...}</c>,
- are considered experimental during OTP 17.</p>
- <p>No type information of maps pairs, only the containing map types,
- are used by Dialyzer in OTP 17.</p>
- </note>
</section>
<section>